diff --git a/packages/framework/src/agent.ts b/packages/framework/src/agent.ts index 7397bbb..df84709 100644 --- a/packages/framework/src/agent.ts +++ b/packages/framework/src/agent.ts @@ -3,7 +3,7 @@ import { zodFunction, zodResponseFormat } from 'openai/helpers/zod.js' import { z } from 'zod' import { openai, Provider } from './models.js' -import { finish, WorkflowState } from './state.js' +import { finish, request, response, WorkflowState } from './state.js' import { Tool } from './tool.js' import { Message } from './types.js' import { Workflow } from './workflow.js' @@ -39,7 +39,7 @@ export const agent = (options: AgentOptions = {}): Agent => { ) : [] - const response = await provider.completions({ + const res = await provider.completions({ messages: [ { role: 'system', @@ -56,18 +56,9 @@ export const agent = (options: AgentOptions = {}): Agent => { Try to complete the task on your own. `, }, - { - role: 'assistant', - content: 'What have been done so far?', - }, - { - role: 'user', - content: `Here is all the work done so far by other agents: ${JSON.stringify(context)}`, - }, - { - role: 'assistant', - content: 'What do you want me to do now?', - }, + response('What have been done so far?'), + request(`Here is all the work done so far by other agents: ${JSON.stringify(context)}`), + response('What do you want me to do now?'), ...state.messages, ], tools: mappedTools.length > 0 ? mappedTools : undefined, @@ -94,40 +85,30 @@ export const agent = (options: AgentOptions = {}): Agent => { ), }) - if (response.choices[0].message.tool_calls.length > 0) { + if (res.choices[0].message.tool_calls.length > 0) { return { ...state, status: 'paused', - messages: state.messages.concat(response.choices[0].message), + messages: state.messages.concat(res.choices[0].message), } } - const result = response.choices[0].message.parsed - if (!result) { + const message = res.choices[0].message.parsed + if (!message) { throw new Error('No parsed response received') } - if (result.response.kind === 'error') { - throw new Error(result.response.reasoning) + if (message.response.kind === 'error') { + throw new Error(message.response.reasoning) } - const agentResponse = { - role: 'assistant' as const, - content: result.response.result, - } + const agentResponse = response(message.response.result) - if (result.response.nextStep) { + if (message.response.nextStep) { return { ...state, status: 'running', - messages: [ - ...state.messages, - agentResponse, - { - role: 'user', - content: result.response.nextStep, - }, - ], + messages: [...state.messages, agentResponse, request(message.response.nextStep)], } } diff --git a/packages/framework/src/agents/final_boss.ts b/packages/framework/src/agents/final_boss.ts index b9ea6d8..5c87a72 100644 --- a/packages/framework/src/agents/final_boss.ts +++ b/packages/framework/src/agents/final_boss.ts @@ -3,11 +3,11 @@ import { zodResponseFormat } from 'openai/helpers/zod' import { z } from 'zod' import { agent, AgentOptions } from '../agent.js' -import { finish } from '../state.js' +import { finish, request, response } from '../state.js' const defaults: AgentOptions = { run: async (state, context, workflow) => { - const response = await workflow.team[state.agent].provider.completions({ + const res = await workflow.team[state.agent].provider.completions({ messages: [ { role: 'system', @@ -16,13 +16,10 @@ const defaults: AgentOptions = { `, }, ...context, - { - role: 'user', - content: s` - Please summarize all executed steps and do your best to achieve - the main goal while responding with the final answer - `, - }, + request(s` + Please summarize all executed steps and do your best to achieve + the main goal while responding with the final answer + `), ], response_format: zodResponseFormat( z.object({ @@ -31,11 +28,11 @@ const defaults: AgentOptions = { 'task_result' ), }) - const result = response.choices[0].message.parsed - if (!result) { + const message = res.choices[0].message.parsed + if (!message) { throw new Error('No parsed response received') } - return finish(state, { role: 'assistant', content: result.finalAnswer }) + return finish(state, response(message.finalAnswer)) }, } diff --git a/packages/framework/src/agents/resource_planner.ts b/packages/framework/src/agents/resource_planner.ts index 9f6c017..5fd4652 100644 --- a/packages/framework/src/agents/resource_planner.ts +++ b/packages/framework/src/agents/resource_planner.ts @@ -3,11 +3,11 @@ import { zodResponseFormat } from 'openai/helpers/zod' import { z } from 'zod' import { agent, AgentOptions } from '../agent.js' -import { handoff } from '../state.js' +import { handoff, request, response } from '../state.js' const defaults: AgentOptions = { run: async (state, context, workflow) => { - const response = await workflow.team[state.agent].provider.completions({ + const res = await workflow.team[state.agent].provider.completions({ messages: [ { role: 'system', @@ -22,21 +22,14 @@ const defaults: AgentOptions = { 4. Previous task context if available `, }, - { - role: 'user', - content: s` + request(s` Here are the available agents: ${Object.entries(workflow.team).map(([name, agent]) => agent.description ? `${agent.description}` : '' )} - - `, - }, - { - role: 'assistant', - content: 'What is the task?', - }, + `), + response('What is the task?'), ...state.messages, ], temperature: 0.1, @@ -49,12 +42,12 @@ const defaults: AgentOptions = { ), }) - const content = response.choices[0].message.parsed - if (!content) { + const message = res.choices[0].message.parsed + if (!message) { throw new Error('No content in response') } - return handoff(state, content.agent, state.messages) + return handoff(state, message.agent, state.messages) }, } diff --git a/packages/framework/src/agents/supervisor.ts b/packages/framework/src/agents/supervisor.ts index 52c392d..98b1cc1 100644 --- a/packages/framework/src/agents/supervisor.ts +++ b/packages/framework/src/agents/supervisor.ts @@ -4,13 +4,13 @@ import { z } from 'zod' import { agent, AgentOptions } from '../agent.js' import { getSteps } from '../messages.js' -import { delegate } from '../state.js' +import { delegate, request, response } from '../state.js' const defaults: AgentOptions = { run: async (state, context, workflow) => { - const [request, ...messages] = state.messages + const [workflowRequest, ...messages] = state.messages - const response = await workflow.team[state.agent].provider.completions({ + const res = await workflow.team[state.agent].provider.completions({ messages: [ { role: 'system', @@ -28,15 +28,9 @@ const defaults: AgentOptions = { 5. Use context from completed tasks to inform next steps `, }, - { - role: 'assistant', - content: 'What is the request?', - }, - request, - { - role: 'assistant', - content: 'What has been completed so far?', - }, + response('What is the request?'), + workflowRequest, + response('What has been completed so far?'), ...getSteps(messages), ], temperature: 0.2, @@ -56,7 +50,7 @@ const defaults: AgentOptions = { }) try { - const content = response.choices[0].message.parsed + const content = res.choices[0].message.parsed if (!content) { throw new Error('No content in response') } @@ -70,13 +64,7 @@ const defaults: AgentOptions = { return delegate( state, - content.tasks.map((item) => [ - 'resourcePlanner', - { - role: 'user', - content: item.task, - }, - ]) + content.tasks.map((item) => ['resourcePlanner', request(item.task)]) ) } catch (error) { throw new Error('Failed to determine next task') diff --git a/packages/framework/src/iterate.ts b/packages/framework/src/iterate.ts index 510108f..5fa1c3d 100644 --- a/packages/framework/src/iterate.ts +++ b/packages/framework/src/iterate.ts @@ -1,4 +1,4 @@ -import { childState, finish, WorkflowState } from './state.js' +import { childState, finish, response, WorkflowState } from './state.js' import { runTools } from './tool_calls.js' import { Message } from './types.js' import { Workflow } from './workflow.js' @@ -48,10 +48,7 @@ export async function run( try { return agent.run(state, context, workflow) } catch (error) { - return finish(state, { - role: 'assistant', - content: error instanceof Error ? error.message : 'Unknown error', - }) + return finish(state, response(error instanceof Error ? error.message : 'Unknown error')) } } diff --git a/packages/framework/src/state.ts b/packages/framework/src/state.ts index e75d466..214bf1f 100644 --- a/packages/framework/src/state.ts +++ b/packages/framework/src/state.ts @@ -25,14 +25,11 @@ export const rootState = (workflow: Workflow): WorkflowState => childState({ agent: 'supervisor', messages: [ - { - role: 'user', - content: s` - Here is description of my workflow and expected output: - ${workflow.description} - ${workflow.output} - `, - }, + request(s` + Here is description of my workflow and expected output: + ${workflow.description} + ${workflow.output} + `), ], }) @@ -73,3 +70,17 @@ export const handoff = ( messages, }) } + +export const response = (content: string): Message => { + return { + role: 'assistant', + content, + } +} + +export const request = (content: string): Message => { + return { + role: 'user', + content, + } +}