From 8f09c35920ec1a8d2e15fab3ba25d22b7a20ab85 Mon Sep 17 00:00:00 2001 From: Abdurrehman Subhani Date: Thu, 19 Dec 2024 16:11:50 +0500 Subject: [PATCH 1/9] init data source manager --- .../classes/data-source-manager.ts | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 packages/usdk/packages/upstreet-agent/packages/react-agents/classes/data-source-manager.ts diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/data-source-manager.ts b/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/data-source-manager.ts new file mode 100644 index 000000000..8436d9a7e --- /dev/null +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/data-source-manager.ts @@ -0,0 +1,44 @@ +interface DataSource { + id: string; + type: string; + fetch(): Promise; +} + +export class DataSourceManager { + private dataSources: Map; + + constructor() { + this.dataSources = new Map(); + } + + addDataSource(dataSource: DataSource): void { + this.dataSources.set(dataSource.id, dataSource); + } + + removeDataSource(id: string): boolean { + return this.dataSources.delete(id); + } + + async fetchAllData(): Promise> { + const results: Record = {}; + + for (const [id, source] of this.dataSources) { + try { + results[id] = await source.fetch(); + } catch (error) { + console.error(`Error fetching data from source ${id}:`, error); + } + } + + return results; + } + + getDataSource(id: string): DataSource | undefined { + return this.dataSources.get(id); + } + + getAllDataSources(): DataSource[] { + return Array.from(this.dataSources.values()); + } +} + From 56fa57a3dded0605614cf06c94125d5b5a746333 Mon Sep 17 00:00:00 2001 From: Abdurrehman Subhani Date: Thu, 19 Dec 2024 16:21:20 +0500 Subject: [PATCH 2/9] improve types and manager --- .../classes/data-source-manager.ts | 38 +++++++------------ .../react-agents/types/react-agents.d.ts | 25 ++++++++++++ 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/data-source-manager.ts b/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/data-source-manager.ts index 8436d9a7e..6b8037c2b 100644 --- a/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/data-source-manager.ts +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/data-source-manager.ts @@ -1,44 +1,34 @@ -interface DataSource { - id: string; - type: string; - fetch(): Promise; -} +import type { BaseDataSource } from '../types/react-agents'; export class DataSourceManager { - private dataSources: Map; + private dataSources: Map; constructor() { this.dataSources = new Map(); } - addDataSource(dataSource: DataSource): void { - this.dataSources.set(dataSource.id, dataSource); + addDataSource(dataSource: BaseDataSource): void { + this.dataSources.set(dataSource.name, dataSource); } removeDataSource(id: string): boolean { return this.dataSources.delete(id); } - async fetchAllData(): Promise> { - const results: Record = {}; - - for (const [id, source] of this.dataSources) { - try { - results[id] = await source.fetch(); - } catch (error) { - console.error(`Error fetching data from source ${id}:`, error); - } - } - - return results; - } - - getDataSource(id: string): DataSource | undefined { + getDataSource(id: string): BaseDataSource | undefined { return this.dataSources.get(id); } - getAllDataSources(): DataSource[] { + getAllDataSources(): BaseDataSource[] { return Array.from(this.dataSources.values()); } + + pullFromDataSource(id: string, args: object): Promise { + const source = this.getDataSource(id); + if (!source) { + throw new Error(`Data source ${id} not found`); + } + return source.pull(args); + } } diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/types/react-agents.d.ts b/packages/usdk/packages/upstreet-agent/packages/react-agents/types/react-agents.d.ts index fee90b32a..36d6d3d91 100644 --- a/packages/usdk/packages/upstreet-agent/packages/react-agents/types/react-agents.d.ts +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/types/react-agents.d.ts @@ -90,6 +90,31 @@ export type ActionStep = { thought?: string; }; +// data sources + +export type DataSourceType = 'api' | 'text' | 'pdf'; + +export interface BaseDataSource { + name: string; + description: string; + pull(args: object): Promise; +} + +export type DataSourceConfig = { + id: string; + type: DataSourceType; + name: string; + description: string; +}; + +export type DataSourceManager = EventTarget & { + addDataSource: (source: BaseDataSource) => void; + removeDataSource: (id: string) => boolean; + getDataSource: (id: string) => BaseDataSource | undefined; + getAllDataSources: () => BaseDataSource[]; + pullFromDataSource: (id: string, args: object) => Promise; +}; + // messages export type ChatMessage = { From 009088ea4a40a1e22b7070991e247c098495a57b Mon Sep 17 00:00:00 2001 From: Abdurrehman Subhani Date: Thu, 19 Dec 2024 17:30:25 +0500 Subject: [PATCH 3/9] add data source renderers, add example api data source --- .../classes/active-agent-object.ts | 4 +- .../react-agents/classes/agent-object.ts | 11 ++++- .../classes/api-data-source-manager.ts | 46 +++++++++++++++++++ .../components/plugins/api-data-source.tsx | 23 ++++++++++ .../components/util/config-components.tsx | 11 +++++ .../react-agents/types/react-agents.d.ts | 19 ++++++++ .../util/data-source-renderer.tsx | 20 ++++++++ 7 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 packages/usdk/packages/upstreet-agent/packages/react-agents/classes/api-data-source-manager.ts create mode 100644 packages/usdk/packages/upstreet-agent/packages/react-agents/components/plugins/api-data-source.tsx create mode 100644 packages/usdk/packages/upstreet-agent/packages/react-agents/util/data-source-renderer.tsx diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/active-agent-object.ts b/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/active-agent-object.ts index c5b32cf51..b509e0ba3 100644 --- a/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/active-agent-object.ts +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/active-agent-object.ts @@ -39,6 +39,7 @@ import { } from './live-manager'; import { PingManager } from './ping-manager'; import { AgentRegistry } from './render-registry'; +import { DataSourceManager } from './data-source-manager'; // @@ -57,7 +58,7 @@ export class ActiveAgentObject extends AgentObject { liveManager: LiveManager; pingManager: PingManager; generativeAgentsMap = new WeakMap(); - + dataSourceManager: DataSourceManager; // constructor( @@ -100,6 +101,7 @@ export class ActiveAgentObject extends AgentObject { this.liveManager = new LiveManager({ agent: this, }); + this.dataSourceManager = new DataSourceManager(); const bindLiveManager = () => { // dispatch up to the registry so the runtime can update its bookkeeping const proxyRegistryEvent = (event: MessageEvent) => { diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/agent-object.ts b/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/agent-object.ts index ce5347142..3cc91b12c 100644 --- a/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/agent-object.ts +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/agent-object.ts @@ -1,4 +1,7 @@ -import { AgentObjectData } from '../types'; +import { + AgentObjectData, + DataSourceConfig, +} from '../types'; export class AgentObject extends EventTarget { id: string; @@ -13,6 +16,8 @@ export class AgentObject extends EventTarget { features: string[]; address: string; stripeConnectAccountId: string; + dataSources?: Record; + constructor(config: AgentObjectData) { super(); @@ -31,6 +36,7 @@ export class AgentObject extends EventTarget { features, address, stripeConnectAccountId, + dataSources, }: AgentObjectData) { this.id = id; this.ownerId = ownerId; @@ -44,5 +50,6 @@ export class AgentObject extends EventTarget { this.features = features; this.address = address; this.stripeConnectAccountId = stripeConnectAccountId; + this.dataSources = dataSources; } -} \ No newline at end of file +} diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/api-data-source-manager.ts b/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/api-data-source-manager.ts new file mode 100644 index 000000000..c4fd4f15e --- /dev/null +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/api-data-source-manager.ts @@ -0,0 +1,46 @@ +import { APIDataSourceProps } from "../types/react-agents"; +import { BaseDataSource } from '../types/react-agents'; + +export class APIDataSourceManager implements BaseDataSource { + id: string; + type: 'api'; + name: string; + description: string; + endpoint: string; + headers?: Record; + params?: Record; + + constructor(config: APIDataSourceProps) { + this.id = config.id; + this.type = 'api'; + this.name = config.name || config.id; + this.description = config.description || ''; + this.endpoint = config.endpoint; + this.headers = config.headers; + this.params = config.params; + } + + async pull(args: object = {}): Promise { + try { + const url = new URL(this.endpoint); + const params = { ...this.params, ...args }; + + Object.entries(params || {}).forEach(([key, value]) => { + url.searchParams.append(key, String(value)); + }); + + const response = await fetch(url.toString(), { + headers: this.headers, + }); + + if (!response.ok) { + throw new Error(`API request failed: ${response.statusText}`); + } + + return response.json(); + } catch (error) { + console.error(`Error fetching from API ${this.id}:`, error); + throw error; + } + } +} \ No newline at end of file diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/components/plugins/api-data-source.tsx b/packages/usdk/packages/upstreet-agent/packages/react-agents/components/plugins/api-data-source.tsx new file mode 100644 index 000000000..9ae98d537 --- /dev/null +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/components/plugins/api-data-source.tsx @@ -0,0 +1,23 @@ +import { + BaseDataSource, + DataSourceType, + APIDataSourceProps, +} from '../../types/react-agents'; +import React, { useEffect } from 'react'; +import { useAgent } from '../../hooks'; +import { APIDataSourceManager } from '../../classes/api-data-source-manager'; + + +export const APIDataSource: React.FC = (props) => { + const agent = useAgent(); + + useEffect(() => { + const dataSource = new APIDataSourceManager(props); + agent.dataSourceManager.addDataSource(dataSource); + return () => { + agent.dataSourceManager.removeDataSource(dataSource.id); + }; + }, [props.endpoint, JSON.stringify(props.headers), JSON.stringify(props.params)]); + + return null; +}; \ No newline at end of file diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/components/util/config-components.tsx b/packages/usdk/packages/upstreet-agent/packages/react-agents/components/util/config-components.tsx index c3fd108d7..db8dc9833 100644 --- a/packages/usdk/packages/upstreet-agent/packages/react-agents/components/util/config-components.tsx +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/components/util/config-components.tsx @@ -5,6 +5,9 @@ import type { import { featureRenderers, } from '../../util/agent-features-renderer'; +import { + dataSourceRenderers, +} from '../../util/data-source-renderer'; // defaults @@ -18,6 +21,8 @@ type ConfigAgentComponentProps = { */ export const ConfigAgentComponents = (props: ConfigAgentComponentProps) => { const features = props.config?.features ?? {}; + const dataSources = props.config?.dataSources ?? {}; + return ( <> {Object.keys(features).map((key) => { @@ -27,6 +32,12 @@ export const ConfigAgentComponents = (props: ConfigAgentComponentProps) => { ); })} + + {Object.entries(dataSources).map(([key, config]) => { + const DataSourceRenderer = dataSourceRenderers[config.type]; + if (!DataSourceRenderer) return null; + return ; + })} ); }; \ No newline at end of file diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/types/react-agents.d.ts b/packages/usdk/packages/upstreet-agent/packages/react-agents/types/react-agents.d.ts index 36d6d3d91..04e014c4f 100644 --- a/packages/usdk/packages/upstreet-agent/packages/react-agents/types/react-agents.d.ts +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/types/react-agents.d.ts @@ -39,6 +39,7 @@ export type AgentObjectData = { features?: string[]; address?: string; stripeConnectAccountId?: string; + dataSources?: Record; }; export type AgentObject = EventTarget & AgentObjectData & { setConfig(config: AgentObjectData): void; @@ -94,7 +95,15 @@ export type ActionStep = { export type DataSourceType = 'api' | 'text' | 'pdf'; +export interface APIDataSource extends BaseDataSource { + type: 'api'; + endpoint: string; + headers?: Record; + params?: Record; +} export interface BaseDataSource { + id: string; + type: DataSourceType; name: string; description: string; pull(args: object): Promise; @@ -115,6 +124,15 @@ export type DataSourceManager = EventTarget & { pullFromDataSource: (id: string, args: object) => Promise; }; +export interface APIDataSourceProps { + id: string; + name?: string; + description?: string; + endpoint: string; + headers?: Record; + params?: Record; +} + // messages export type ChatMessage = { @@ -482,6 +500,7 @@ export type ActiveAgentObject = AgentObject & { pingManager: PingManager; liveManager: LiveManager; generativeAgentsMap: WeakMap; + dataSourceManager: DataSourceManager; // diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/util/data-source-renderer.tsx b/packages/usdk/packages/upstreet-agent/packages/react-agents/util/data-source-renderer.tsx new file mode 100644 index 000000000..c1b33f260 --- /dev/null +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/util/data-source-renderer.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { APIDataSource } from '../components/plugins/api-data-source'; + +export const dataSourceRenderers = { + api: ({ id, name, description, endpoint, headers, params }) => { + if (endpoint) { + return ( + + ); + } + return null; + }, +}; \ No newline at end of file From 31c559b8e4f13ef7384d2e206c805bbd9fae6a73 Mon Sep 17 00:00:00 2001 From: Abdurrehman Subhani Date: Thu, 19 Dec 2024 17:35:25 +0500 Subject: [PATCH 4/9] add DataSourcesPrompt prompt to add to agent's prompt context --- .../components/util/default-components.tsx | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/components/util/default-components.tsx b/packages/usdk/packages/upstreet-agent/packages/react-agents/components/util/default-components.tsx index 364df23f2..968ea3f6f 100644 --- a/packages/usdk/packages/upstreet-agent/packages/react-agents/components/util/default-components.tsx +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/components/util/default-components.tsx @@ -74,6 +74,7 @@ const DefaultPrompts = () => { + @@ -91,6 +92,28 @@ const DefaultHeaderPrompt = () => { ); }; +const DataSourcesPrompt = () => { + const agent = useAgent(); + const dataSources = agent.dataSourceManager.getAllDataSources(); + + if (dataSources.length === 0) return null; + + return ( + + {dedent` + # Data Sources + You have access to the following data sources that you can query: + ${dataSources.map(source => dedent` + - ${source.name} (ID: ${source.id}) + Description: ${source.description} + Type: ${source.type} + ${source.type === 'api' ? `Endpoint: ${(source as any).endpoint}` : ''} + `).join('\n')} + `} + + ); +}; + const ConversationEnvironmentPrompt = () => { return ( <> From 9595b79978e2d96e93629f744f1b4c08c574a1ea Mon Sep 17 00:00:00 2001 From: Abdurrehman Subhani Date: Thu, 19 Dec 2024 21:42:58 +0500 Subject: [PATCH 5/9] add data source learner component having prompt auto task and action, fix addDataSource to use id instead of name --- .../classes/data-source-manager.ts | 2 +- .../plugins/data-source-learner.tsx | 111 ++++++++++++++++++ .../packages/react-agents/index.tsx | 1 + 3 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 packages/usdk/packages/upstreet-agent/packages/react-agents/components/plugins/data-source-learner.tsx diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/data-source-manager.ts b/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/data-source-manager.ts index 6b8037c2b..c97e93b54 100644 --- a/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/data-source-manager.ts +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/data-source-manager.ts @@ -8,7 +8,7 @@ export class DataSourceManager { } addDataSource(dataSource: BaseDataSource): void { - this.dataSources.set(dataSource.name, dataSource); + this.dataSources.set(dataSource.id, dataSource); } removeDataSource(id: string): boolean { diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/components/plugins/data-source-learner.tsx b/packages/usdk/packages/upstreet-agent/packages/react-agents/components/plugins/data-source-learner.tsx new file mode 100644 index 000000000..373fc5f0f --- /dev/null +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/components/plugins/data-source-learner.tsx @@ -0,0 +1,111 @@ +import React from 'react'; +import { Action } from '../core/action'; +import { Prompt } from '../core/prompt'; +import { useAgent } from '../../hooks'; +import { z } from 'zod'; +import dedent from 'dedent'; +import type { PendingActionEvent } from '../../types'; +import { AutoTask } from './auto-task'; + +export const DataSourceLearner: React.FC = () => { + const agent = useAgent(); + + return ( + <> + + {dedent`\ + # Data Source Learning System + + You can learn from available data sources and store the knowledge in your memory. + Use the queryAndLearn action when you need to: + - Gather new information about a topic + - Verify or update your existing knowledge + - Get real-time data for user queries + + Available data sources: + ${agent.dataSourceManager.getAllDataSources().map(source => dedent` + - ${source.name} (ID: ${source.id}) + ${source.description} + `).join('\n')} + `} + + + + { + const { message, agent } = e.data; + const { args } = message; + + try { + // Query the data source + const data = await agent.agent.dataSourceManager.pullFromDataSource( + args.sourceId, + args.args + ); + + console.log('data: ', data); + + // Analyze and summarize the data + const dataSummary = await agent.complete([ + { + role: 'user', + content: dedent`\ + Analyze and summarize the following data from ${args.sourceId}: + \`\`\` + ${JSON.stringify(data, null, 2)} + \`\`\` + + Context for why this data was queried: ${args.reason} + + Provide a concise summary focusing on the most relevant and interesting information. + `, + }, + ], { + model: agent.agent.smallModel, + }); + + console.log('data summary: ', dataSummary); + // Store the learned information in memory + await agent.agent.addMemory( + `Learned from ${args.sourceId}: ${dataSummary.content}`, + { + sourceId: args.sourceId, + query: args.args, + rawData: data, + summary: dataSummary.content, + timestamp: Date.now(), + reason: args.reason + } + ); + + await e.commit(); + } catch (error) { + console.error('Error in queryAndLearn:', error); + throw error; + } + }} + /> + + ); +}; \ No newline at end of file diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/index.tsx b/packages/usdk/packages/upstreet-agent/packages/react-agents/index.tsx index eb2d915e7..b7ede25ab 100644 --- a/packages/usdk/packages/upstreet-agent/packages/react-agents/index.tsx +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/index.tsx @@ -25,5 +25,6 @@ export * from './components/plugins/video-perception'; export * from './loops/chat-loop'; export * from './loops/action-loop'; export * from './components/plugins/auto-task'; +export * from './components/plugins/data-source-learner'; export * from './hooks'; From 0347ba83f3b7473feb53e34361940098663eeb7f Mon Sep 17 00:00:00 2001 From: Abdurrehman Subhani Date: Fri, 20 Dec 2024 11:18:50 +0500 Subject: [PATCH 6/9] data source args schema --- .../classes/api-data-source-manager.ts | 2 ++ .../plugins/data-source-learner.tsx | 23 ++++++++++--------- .../react-agents/types/react-agents.d.ts | 2 ++ .../util/data-source-renderer.tsx | 3 ++- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/api-data-source-manager.ts b/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/api-data-source-manager.ts index c4fd4f15e..2c1aef051 100644 --- a/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/api-data-source-manager.ts +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/api-data-source-manager.ts @@ -9,6 +9,7 @@ export class APIDataSourceManager implements BaseDataSource { endpoint: string; headers?: Record; params?: Record; + requiredArgs?: string[]; constructor(config: APIDataSourceProps) { this.id = config.id; @@ -18,6 +19,7 @@ export class APIDataSourceManager implements BaseDataSource { this.endpoint = config.endpoint; this.headers = config.headers; this.params = config.params; + this.requiredArgs = config.requiredArgs; } async pull(args: object = {}): Promise { diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/components/plugins/data-source-learner.tsx b/packages/usdk/packages/upstreet-agent/packages/react-agents/components/plugins/data-source-learner.tsx index 373fc5f0f..fd29cca3f 100644 --- a/packages/usdk/packages/upstreet-agent/packages/react-agents/components/plugins/data-source-learner.tsx +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/components/plugins/data-source-learner.tsx @@ -10,6 +10,7 @@ import { AutoTask } from './auto-task'; export const DataSourceLearner: React.FC = () => { const agent = useAgent(); + /// XXX TODO: NEED TO ENFORCE API SCHEME BETTER, PERHAPS USE OPENAPI SPEC APPROACH return ( <> @@ -26,7 +27,11 @@ export const DataSourceLearner: React.FC = () => { ${agent.dataSourceManager.getAllDataSources().map(source => dedent` - ${source.name} (ID: ${source.id}) ${source.description} - `).join('\n')} + REQUIRED: You must include these arguments in your query: + ${source.requiredArgs?.map(arg => ` - '${arg}': (string) REQUIRED`) + .join('\n') || ' - No required arguments'}`).join('\n')} + + NOTE: Queries will fail if required arguments are not provided! `} @@ -35,22 +40,20 @@ export const DataSourceLearner: React.FC = () => { type="queryAndLearn" description={dedent`\ Query a data source and store the learned information in memory. - Use this to expand your knowledge base with data from available sources. + IMPORTANT: You must provide all required arguments for the data source! `} schema={ z.object({ sourceId: z.string(), - args: z.object({}), - reason: z.string(), + jsonArgs: z.string(), + reason: z.string() }) } examples={[ { sourceId: "weather-api", - args: { - q: "London" - }, - reason: "Learning about current weather conditions in London to provide accurate weather information", + jsonArgs: "{\"q\": \"London\"}", + reason: "Learning about current weather conditions in London", } ]} handler={async (e: PendingActionEvent) => { @@ -61,7 +64,7 @@ export const DataSourceLearner: React.FC = () => { // Query the data source const data = await agent.agent.dataSourceManager.pullFromDataSource( args.sourceId, - args.args + JSON.parse(args.jsonArgs) ); console.log('data: ', data); @@ -98,8 +101,6 @@ export const DataSourceLearner: React.FC = () => { reason: args.reason } ); - - await e.commit(); } catch (error) { console.error('Error in queryAndLearn:', error); throw error; diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/types/react-agents.d.ts b/packages/usdk/packages/upstreet-agent/packages/react-agents/types/react-agents.d.ts index 04e014c4f..a6caa8d98 100644 --- a/packages/usdk/packages/upstreet-agent/packages/react-agents/types/react-agents.d.ts +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/types/react-agents.d.ts @@ -107,6 +107,7 @@ export interface BaseDataSource { name: string; description: string; pull(args: object): Promise; + requiredArgs?: string[]; } export type DataSourceConfig = { @@ -131,6 +132,7 @@ export interface APIDataSourceProps { endpoint: string; headers?: Record; params?: Record; + requiredArgs?: string[]; } // messages diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/util/data-source-renderer.tsx b/packages/usdk/packages/upstreet-agent/packages/react-agents/util/data-source-renderer.tsx index c1b33f260..d02437030 100644 --- a/packages/usdk/packages/upstreet-agent/packages/react-agents/util/data-source-renderer.tsx +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/util/data-source-renderer.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { APIDataSource } from '../components/plugins/api-data-source'; export const dataSourceRenderers = { - api: ({ id, name, description, endpoint, headers, params }) => { + api: ({ id, name, description, endpoint, headers, params, requiredArgs }) => { if (endpoint) { return ( ); } From 41aff495825947250af37977caed4cc8ed9d0d23 Mon Sep 17 00:00:00 2001 From: Abdurrehman Subhani Date: Mon, 6 Jan 2025 15:29:56 +0500 Subject: [PATCH 7/9] throw exception if data source type is undefined --- .../react-agents/components/util/config-components.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/components/util/config-components.tsx b/packages/usdk/packages/upstreet-agent/packages/react-agents/components/util/config-components.tsx index db8dc9833..fd2cbf51f 100644 --- a/packages/usdk/packages/upstreet-agent/packages/react-agents/components/util/config-components.tsx +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/components/util/config-components.tsx @@ -34,6 +34,9 @@ export const ConfigAgentComponents = (props: ConfigAgentComponentProps) => { })} {Object.entries(dataSources).map(([key, config]) => { + if (!config.type) { + throw new Error(`Data source ${key} is missing required 'type' field`); + } const DataSourceRenderer = dataSourceRenderers[config.type]; if (!DataSourceRenderer) return null; return ; From 8e0ad8c9044311ab948a06b6de72b087f00b90d3 Mon Sep 17 00:00:00 2001 From: Abdurrehman Subhani Date: Mon, 6 Jan 2025 15:58:17 +0500 Subject: [PATCH 8/9] add api data source checking --- .../classes/api-data-source-manager.ts | 2 + .../plugins/data-source-learner.tsx | 2 +- .../components/util/default-components.tsx | 11 +---- .../react-agents/types/react-agents.d.ts | 1 + .../util/data-source-renderer.tsx | 44 +++++++++++++------ .../packages/react-agents/util/fetch.mjs | 3 ++ 6 files changed, 39 insertions(+), 24 deletions(-) diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/api-data-source-manager.ts b/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/api-data-source-manager.ts index 2c1aef051..e5adeb92a 100644 --- a/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/api-data-source-manager.ts +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/api-data-source-manager.ts @@ -10,6 +10,7 @@ export class APIDataSourceManager implements BaseDataSource { headers?: Record; params?: Record; requiredArgs?: string[]; + examples?: string[]; constructor(config: APIDataSourceProps) { this.id = config.id; @@ -20,6 +21,7 @@ export class APIDataSourceManager implements BaseDataSource { this.headers = config.headers; this.params = config.params; this.requiredArgs = config.requiredArgs; + this.examples = config.examples; } async pull(args: object = {}): Promise { diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/components/plugins/data-source-learner.tsx b/packages/usdk/packages/upstreet-agent/packages/react-agents/components/plugins/data-source-learner.tsx index fd29cca3f..c4f7e3305 100644 --- a/packages/usdk/packages/upstreet-agent/packages/react-agents/components/plugins/data-source-learner.tsx +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/components/plugins/data-source-learner.tsx @@ -37,7 +37,7 @@ export const DataSourceLearner: React.FC = () => { { - ${source.name} (ID: ${source.id}) Description: ${source.description} Type: ${source.type} - ${source.type === 'api' ? `Endpoint: ${(source as any).endpoint}` : ''} + ${source.type === 'api' ? `Required args: ${(source as any).requiredArgs}` : ''} + ${source.type === 'api' ? `Examples: ${(source as any).examples}` : ''} `).join('\n')} `} @@ -343,14 +344,6 @@ const InstructionsPrompt = () => { # Instructions Respond with the next action taken by your character: ${agent.name} The method/args of your response must match one of the allowed actions. - - Before choosing an action, decide if you should respond at all: - - Return null (no action) if: - * Message is clearly meant for others (unless you have crucial information) - * Your input wouldn't add value to the conversation - * The conversation is naturally concluding - * You've already responded frequently in the last few messages (2-3 messages max) - * Multiple other agents are already actively participating `} ); diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/types/react-agents.d.ts b/packages/usdk/packages/upstreet-agent/packages/react-agents/types/react-agents.d.ts index 5717d99cb..9189141c6 100644 --- a/packages/usdk/packages/upstreet-agent/packages/react-agents/types/react-agents.d.ts +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/types/react-agents.d.ts @@ -130,6 +130,7 @@ export interface APIDataSourceProps { name?: string; description?: string; endpoint: string; + examples: string[]; headers?: Record; params?: Record; requiredArgs?: string[]; diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/util/data-source-renderer.tsx b/packages/usdk/packages/upstreet-agent/packages/react-agents/util/data-source-renderer.tsx index d02437030..8cdca74ab 100644 --- a/packages/usdk/packages/upstreet-agent/packages/react-agents/util/data-source-renderer.tsx +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/util/data-source-renderer.tsx @@ -2,20 +2,36 @@ import React from 'react'; import { APIDataSource } from '../components/plugins/api-data-source'; export const dataSourceRenderers = { - api: ({ id, name, description, endpoint, headers, params, requiredArgs }) => { - if (endpoint) { - return ( - - ); + api: ({ id, name, description, endpoint, headers, params, requiredArgs, examples }) => { + + const requiredParams = { + id, + name, + description, + endpoint, + requiredArgs, + examples + }; + + const missing = Object.entries(requiredParams) + .filter(([_, value]) => !value) + .map(([key]) => key); + + if (missing.length > 0) { + throw new Error(`Data source ${id || 'unknown'} is missing required parameters: ${missing.join(', ')}`); } - return null; + + return ( + + ); }, }; \ No newline at end of file diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/util/fetch.mjs b/packages/usdk/packages/upstreet-agent/packages/react-agents/util/fetch.mjs index ec934dee2..c5850726e 100644 --- a/packages/usdk/packages/upstreet-agent/packages/react-agents/util/fetch.mjs +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/util/fetch.mjs @@ -415,6 +415,8 @@ export const fetchJsonCompletion = async ({ throw new Error('no jwt'); } + console.log('fetchJsonCompletion', messages); + const match = model.match(/^([^:]+?):/); if (match) { const modelType = match[1]; @@ -430,6 +432,7 @@ export const fetchJsonCompletion = async ({ }, { jwt, }); + console.log('result', result); return result; } else { throw new Error('invalid model type: ' + JSON.stringify(modelType)); From 479667e868ecf45d064f1d06d9b7b97a8fd52b3b Mon Sep 17 00:00:00 2001 From: Abdurrehman Subhani Date: Mon, 6 Jan 2025 17:13:39 +0500 Subject: [PATCH 9/9] enforce api schema for data source --- .../classes/api-data-source-manager.ts | 41 +++-- .../plugins/data-source-learner.tsx | 82 +-------- .../react-agents/types/react-agents.d.ts | 3 +- .../util/data-source-renderer.tsx | 169 ++++++++++++++++-- 4 files changed, 192 insertions(+), 103 deletions(-) diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/api-data-source-manager.ts b/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/api-data-source-manager.ts index e5adeb92a..85c8b32e9 100644 --- a/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/api-data-source-manager.ts +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/classes/api-data-source-manager.ts @@ -1,3 +1,4 @@ +import { z } from "zod"; import { APIDataSourceProps } from "../types/react-agents"; import { BaseDataSource } from '../types/react-agents'; @@ -11,7 +12,8 @@ export class APIDataSourceManager implements BaseDataSource { params?: Record; requiredArgs?: string[]; examples?: string[]; - + schema: z.ZodSchema; + constructor(config: APIDataSourceProps) { this.id = config.id; this.type = 'api'; @@ -22,27 +24,34 @@ export class APIDataSourceManager implements BaseDataSource { this.params = config.params; this.requiredArgs = config.requiredArgs; this.examples = config.examples; + this.schema = config.schema; } async pull(args: object = {}): Promise { - try { - const url = new URL(this.endpoint); - const params = { ...this.params, ...args }; - - Object.entries(params || {}).forEach(([key, value]) => { - url.searchParams.append(key, String(value)); - }); + try { + // Validate args against schema + const validatedArgs = this.schema.parse(args); + + const url = new URL(this.endpoint); + const params = { ...this.params, ...validatedArgs }; + + Object.entries(params || {}).forEach(([key, value]) => { + url.searchParams.append(key, String(value)); + }); - const response = await fetch(url.toString(), { - headers: this.headers, - }); + const response = await fetch(url.toString(), { + headers: this.headers, + }); - if (!response.ok) { - throw new Error(`API request failed: ${response.statusText}`); - } + if (!response.ok) { + throw new Error(`API request failed: ${response.statusText}`); + } - return response.json(); - } catch (error) { + return response.json(); + } catch (error) { + if (error instanceof z.ZodError) { + throw new Error(`Invalid arguments: ${error.message}`); + } console.error(`Error fetching from API ${this.id}:`, error); throw error; } diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/components/plugins/data-source-learner.tsx b/packages/usdk/packages/upstreet-agent/packages/react-agents/components/plugins/data-source-learner.tsx index c4f7e3305..448d19163 100644 --- a/packages/usdk/packages/upstreet-agent/packages/react-agents/components/plugins/data-source-learner.tsx +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/components/plugins/data-source-learner.tsx @@ -6,11 +6,12 @@ import { z } from 'zod'; import dedent from 'dedent'; import type { PendingActionEvent } from '../../types'; import { AutoTask } from './auto-task'; +import { ConversationProvider } from '../core/conversation'; +import { RAGMemory } from './rag-memory'; export const DataSourceLearner: React.FC = () => { const agent = useAgent(); - /// XXX TODO: NEED TO ENFORCE API SCHEME BETTER, PERHAPS USE OPENAPI SPEC APPROACH return ( <> @@ -34,79 +35,14 @@ export const DataSourceLearner: React.FC = () => { NOTE: Queries will fail if required arguments are not provided! `} - - - { - const { message, agent } = e.data; - const { args } = message; - - try { - // Query the data source - const data = await agent.agent.dataSourceManager.pullFromDataSource( - args.sourceId, - JSON.parse(args.jsonArgs) - ); - console.log('data: ', data); - - // Analyze and summarize the data - const dataSummary = await agent.complete([ - { - role: 'user', - content: dedent`\ - Analyze and summarize the following data from ${args.sourceId}: - \`\`\` - ${JSON.stringify(data, null, 2)} - \`\`\` - - Context for why this data was queried: ${args.reason} - - Provide a concise summary focusing on the most relevant and interesting information. - `, - }, - ], { - model: agent.agent.smallModel, - }); - - console.log('data summary: ', dataSummary); - // Store the learned information in memory - await agent.agent.addMemory( - `Learned from ${args.sourceId}: ${dataSummary.content}`, - { - sourceId: args.sourceId, - query: args.args, - rawData: data, - summary: dataSummary.content, - timestamp: Date.now(), - reason: args.reason - } - ); - } catch (error) { - console.error('Error in queryAndLearn:', error); - throw error; - } - }} - /> + {/* add the RAG Memory Component */} + + + + + {/* Add AutoTask for Self-learning from the data sources */} + ); }; \ No newline at end of file diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/types/react-agents.d.ts b/packages/usdk/packages/upstreet-agent/packages/react-agents/types/react-agents.d.ts index 9189141c6..8e8a425f7 100644 --- a/packages/usdk/packages/upstreet-agent/packages/react-agents/types/react-agents.d.ts +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/types/react-agents.d.ts @@ -1,5 +1,5 @@ import type { ReactNode, FC, Ref } from 'react'; -import type { ZodTypeAny } from 'zod'; +import type { z, ZodTypeAny } from 'zod'; // intrinsics @@ -131,6 +131,7 @@ export interface APIDataSourceProps { description?: string; endpoint: string; examples: string[]; + schema: z.ZodSchema; // Add schema property headers?: Record; params?: Record; requiredArgs?: string[]; diff --git a/packages/usdk/packages/upstreet-agent/packages/react-agents/util/data-source-renderer.tsx b/packages/usdk/packages/upstreet-agent/packages/react-agents/util/data-source-renderer.tsx index 8cdca74ab..909290968 100644 --- a/packages/usdk/packages/upstreet-agent/packages/react-agents/util/data-source-renderer.tsx +++ b/packages/usdk/packages/upstreet-agent/packages/react-agents/util/data-source-renderer.tsx @@ -1,15 +1,95 @@ import React from 'react'; import { APIDataSource } from '../components/plugins/api-data-source'; +import { z } from 'zod'; +import { Action } from '../components/core/action'; +import { PendingActionEvent } from '../classes/pending-action-event'; +import dedent from 'dedent'; -export const dataSourceRenderers = { - api: ({ id, name, description, endpoint, headers, params, requiredArgs, examples }) => { +/* + example for adding a API data source in the agent.json: + "dataSources": [ + { + "id": "weather-api", + "name": "Weather API", + "type": "api", + "description": "Use this data source to get the current weather for a location by providing the required arguments", + "params": { + "key": "xxx" + }, + "endpoint": "https://api.weatherapi.com/v1/current.json", + "schema": { + "type": "object", + "properties": { + "q": { + "type": "string", + "description": "Location name or coordinates" + } + }, + "required": ["q"] + }, + "examples": [ + { + "q": "London" + } + ] + }, + { + "id": "pokedex-ability-api", + "name": "Pokédex Ability API", + "type": "api", + "description": "Use this data source to get information about Pokémon abilities by providing their name or ID number", + "endpoint": "https://pokeapi.co/api/v2/ability", + "schema": { + "type": "object", + "properties": { + "pokemon": { + "type": "string", + "description": "Pokemon name or ID number" + } + }, + "required": ["pokemon"] + }, + "examples": [ + { + "pokemon": "pikachu" + }, + { + "pokemon": "charizard" + } + ] + } + ], + +*/ +const zodFromJsonSchema = (schema: any, sourceId: string): z.ZodSchema => { + // Convert JSON schema to Zod schema + if (schema.type === 'object') { + const shape: Record = { + sourceId: z.literal(sourceId), + ...Object.fromEntries( + Object.entries(schema.properties || {}).map(([key, value]: [string, any]) => [ + key, + value.type === 'string' ? z.string() : z.any() + ]) + ) + }; + const baseSchema = z.object(shape); + return schema.required ? + baseSchema.required(['sourceId' as const, ...(schema.required as const[])]) : + baseSchema.required(['sourceId' as const]); + } + return z.any(); +}; + +export const dataSourceRenderers = { + api: ({ id, name, description, endpoint, headers, params, schema, examples }) => { const requiredParams = { id, name, description, endpoint, - requiredArgs, + schema, examples }; @@ -21,17 +101,80 @@ export const dataSourceRenderers = { throw new Error(`Data source ${id || 'unknown'} is missing required parameters: ${missing.join(', ')}`); } + const zodSchema = zodFromJsonSchema(schema, id); + return ( - + <> + + {/* APIDataSourceManager Action, maybe i can merge this action within the APIDataSource */} + { + const { message, agent } = e.data; + const { args } = message; + + try { + // Query the data source + const data = await agent.agent.dataSourceManager.pullFromDataSource( + args.sourceId, + args + ); + + console.log('data: ', data); + + // Analyze and summarize the data + const dataSummary = await agent.complete([ + { + role: 'user', + content: dedent`\ + Analyze and summarize the following data from ${args.sourceId}: + \`\`\` + ${JSON.stringify(data, null, 2)} + \`\`\` + + Context for why this data was queried: ${args.reason} + + Provide a concise summary focusing on the most relevant and interesting information. + `, + }, + ], { + model: agent.agent.smallModel, + }); + + console.log('data summary: ', dataSummary); + // Store the learned information in memory + await agent.agent.addMemory( + `Learned from ${args.sourceId}: ${dataSummary.content}`, + { + sourceId: args.sourceId, + query: args.args, + rawData: data, + summary: dataSummary.content, + timestamp: Date.now(), + reason: args.reason + } + ); + + await e.commit(); + } catch (error) { + console.error('Error in queryAndLearn:', error); + throw error; + } + }} + /> + ); }, }; \ No newline at end of file