Skip to content

Commit

Permalink
chore: update helers
Browse files Browse the repository at this point in the history
  • Loading branch information
grabbou committed Dec 13, 2024
1 parent 23e6a5e commit 538d644
Show file tree
Hide file tree
Showing 10 changed files with 104 additions and 90 deletions.
9 changes: 5 additions & 4 deletions packages/framework/src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@ import s from 'dedent'
import { zodFunction, zodResponseFormat } from 'openai/helpers/zod.js'
import { z } from 'zod'

import { Message, request, response } from './messages.js'
import { openai, Provider } from './models.js'
import { finish, request, response, WorkflowState } from './state.js'
import { finish, WorkflowState } from './state.js'
import { Tool } from './tool.js'
import { Message } from './types.js'
import { Workflow } from './workflow.js'

export type AgentOptions = Partial<Agent>

export type AgentName = string
export type Agent = {
description?: string
tools: {
[key: string]: Tool
[key: AgentName]: Tool
}
provider: Provider
run: (state: WorkflowState, context: Message[], workflow: Workflow) => Promise<WorkflowState>
Expand Down Expand Up @@ -89,7 +90,7 @@ export const agent = (options: AgentOptions = {}): Agent => {
return {
...state,
status: 'paused',
messages: state.messages.concat(res.choices[0].message),
messages: [...state.messages, res.choices[0].message],
}
}

Expand Down
4 changes: 3 additions & 1 deletion packages/framework/src/agents/final_boss.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { zodResponseFormat } from 'openai/helpers/zod'
import { z } from 'zod'

import { agent, AgentOptions } from '../agent.js'
import { finish, request, response } from '../state.js'
import { response } from '../messages.js'
import { request } from '../messages.js'
import { finish } from '../state.js'

const defaults: AgentOptions = {
run: async (state, context, workflow) => {
Expand Down
6 changes: 4 additions & 2 deletions packages/framework/src/agents/resource_planner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { zodResponseFormat } from 'openai/helpers/zod'
import { z } from 'zod'

import { agent, AgentOptions } from '../agent.js'
import { handoff, request, response } from '../state.js'
import { response } from '../messages.js'
import { request } from '../messages.js'
import { handoff } from '../state.js'

const defaults: AgentOptions = {
run: async (state, context, workflow) => {
Expand Down Expand Up @@ -47,7 +49,7 @@ const defaults: AgentOptions = {
throw new Error('No content in response')
}

return handoff(state, message.agent, state.messages)
return handoff(state, message.agent)
},
}

Expand Down
50 changes: 34 additions & 16 deletions packages/framework/src/agents/supervisor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,29 @@ import { zodResponseFormat } from 'openai/helpers/zod'
import { z } from 'zod'

import { agent, AgentOptions } from '../agent.js'
import { getSteps } from '../messages.js'
import { delegate, request, response } from '../state.js'
import { Message, system } from '../messages.js'
import { request, response } from '../messages.js'
import { delegate } from '../state.js'

const defaults: AgentOptions = {
run: async (state, context, workflow) => {
const [workflowRequest, ...messages] = state.messages

const res = await workflow.team[state.agent].provider.completions({
messages: [
{
role: 'system',
content: s`
You are a planner that breaks down complex workflows into smaller, actionable steps.
Your job is to determine the next task that needs to be done based on the <workflow> and what has been completed so far.
system(s`
You are a planner that breaks down complex workflows into smaller, actionable steps.
Your job is to determine the next task that needs to be done based on the <workflow> and what has been completed so far.
You can run tasks in parallel, if they do not depend on each other. Otherwise, run them sequentially.
You can run tasks in parallel, if they do not depend on each other results. Otherwise, run them sequentially.
Rules:
1. Each task should be self-contained and achievable
2. Tasks should be specific and actionable
3. Return null when the workflow is complete
4. Consider dependencies and order of operations
5. Use context from completed tasks to inform next steps
`,
},
Rules:
1. Each task should be self-contained and achievable
2. Tasks should be specific and actionable
3. Return null when the workflow is complete
4. Consider dependencies and order of operations
5. Use context from completed tasks to inform next steps
`),
response('What is the request?'),
workflowRequest,
response('What has been completed so far?'),
Expand Down Expand Up @@ -77,3 +75,23 @@ export const supervisor = (options?: AgentOptions) =>
...defaults,
...options,
})

const getSteps = (conversation: Message[]): Message[] => {
const messagePairs = conversation.reduce(
(pairs: Message[][], message: Message, index: number) => {
if (index % 2 === 0) {
pairs.push([message])
} else {
pairs[pairs.length - 1].push(message)
}
return pairs
},
[]
)
return messagePairs.map(([task, result]) =>
request(s`
Step name: ${task.content}
Step result: ${result.content}
`)
)
}
8 changes: 4 additions & 4 deletions packages/framework/src/iterate.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { childState, finish, response, WorkflowState } from './state.js'
import { Message, response } from './messages.js'
import { childState, finish, WorkflowState } from './state.js'
import { runTools } from './tool_calls.js'
import { Message } from './types.js'
import { Workflow } from './workflow.js'

// tbd: finalize workflow
Expand All @@ -23,7 +23,7 @@ export async function run(
if (children.every((child) => child.status === 'finished')) {
return {
...state,
messages: state.messages.concat(children.flatMap((child) => child.messages)),
messages: [...state.messages, ...children.flatMap((child) => child.messages)],
children: [],
}
}
Expand All @@ -40,7 +40,7 @@ export async function run(
return {
...state,
status: 'running',
messages: state.messages.concat(toolsResponse),
messages: [...state.messages, ...toolsResponse],
}
}

Expand Down
62 changes: 38 additions & 24 deletions packages/framework/src/messages.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,43 @@
import s from 'dedent'
import {
ChatCompletionAssistantMessageParam,
ChatCompletionMessageParam,
ChatCompletionSystemMessageParam,
ChatCompletionToolMessageParam,
ChatCompletionUserMessageParam,
} from 'openai/resources/index.js'

import { Message } from './types.js'
export type Response = ChatCompletionAssistantMessageParam
export const response = (content: string): Response => {
return {
role: 'assistant',
content,
}
}

export type Request = ChatCompletionUserMessageParam
export const request = (content: string): Request => {
return {
role: 'user',
content,
}
}

const groupMessagePairs = <T>(messages: T[]): T[][] => {
return messages.reduce((pairs: T[][], message: T, index: number) => {
if (index % 2 === 0) {
pairs.push([message])
} else {
pairs[pairs.length - 1].push(message)
}
return pairs
}, [])
export type System = ChatCompletionSystemMessageParam
export const system = (content: string): System => {
return {
role: 'system',
content,
}
}

/**
* Chat conversation is stored as an array of [task, result, task, result...] pairs.
* This function groups them and returns them as an array of steps.
*/
export const getSteps = (conversation: Message[]): Message[] => {
const steps = groupMessagePairs(conversation)
return steps.map(([task, result]) => ({
role: 'user' as const,
content: s`
Step name: ${task.content}
Step result: ${result.content}
`,
}))
export type Tool = ChatCompletionToolMessageParam
export const toolResult = (toolCallId: string, content: string): Tool => {
return {
role: 'tool',
tool_call_id: toolCallId,
content,
}
}

export type Message = ChatCompletionMessageParam
export type Conversation = [Request, ...Message[]]
36 changes: 10 additions & 26 deletions packages/framework/src/state.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import s from 'dedent'

import { Message } from './types.js'
import { AgentName } from './agent.js'
import { Conversation, Request, request, Response } from './messages.js'
import { Workflow } from './workflow.js'

type WorkflowStateOptions = {
agent: string
messages: Conversation

status?: 'idle' | 'running' | 'paused' | 'finished' | 'failed'
messages?: Message[]
children?: WorkflowState[]
}

export const childState = (options: WorkflowStateOptions): WorkflowState => {
const { status = 'idle', messages = [], agent, children = [] } = options
const { status = 'idle', messages, agent, children = [] } = options
return {
status,
messages,
Expand All @@ -35,19 +36,20 @@ export const rootState = (workflow: Workflow): WorkflowState =>

export type WorkflowState = Required<WorkflowStateOptions>

export const getRequest = (state: WorkflowState): Message => {
export const getRequest = (state: WorkflowState): Request => {
return state.messages[0]
}

export const finish = (state: WorkflowState, response: Message): WorkflowState => {
export const finish = (state: WorkflowState, response: Response): WorkflowState => {
return {
...state,
status: 'finished',
messages: [getRequest(state), response],
}
}

export const delegate = (state: WorkflowState, requests: [string, Message][]): WorkflowState => {
type DelegationRequest = [AgentName, Request]
export const delegate = (state: WorkflowState, requests: DelegationRequest[]): WorkflowState => {
return {
...state,
status: 'running',
Expand All @@ -60,27 +62,9 @@ export const delegate = (state: WorkflowState, requests: [string, Message][]): W
}
}

export const handoff = (
state: WorkflowState,
agent: string,
messages: Message[]
): WorkflowState => {
export const handoff = (state: WorkflowState, agent: AgentName): WorkflowState => {
return childState({
agent,
messages,
messages: state.messages,
})
}

export const response = (content: string): Message => {
return {
role: 'assistant',
content,
}
}

export const request = (content: string): Message => {
return {
role: 'user',
content,
}
}
3 changes: 2 additions & 1 deletion packages/framework/src/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ export const logger: Telemetry = ({ prevState, nextState }) => {
case 'paused': {
const lastMessage = state.messages.at(-1)!
if (isToolCallRequest(lastMessage)) {
return `Waiting for tools: ${lastMessage.tool_calls.map((toolCall) => toolCall.function.name).join(', ')}`
const tools = lastMessage.tool_calls.map((toolCall) => toolCall.function.name).join(', ')
return `Waiting for tools: ${tools}`
}
return 'Paused'
}
Expand Down
2 changes: 1 addition & 1 deletion packages/framework/src/tool.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import z, { ZodTypeAny } from 'zod'

import { Message } from './messages.js'
import { Provider } from './models.js'
import { Message } from './types.js'

export type Tool<P extends ZodTypeAny = any> = {
description: string
Expand Down
14 changes: 3 additions & 11 deletions packages/framework/src/tool_calls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import type {
} from 'openai/resources/beta/chat/completions'
import { ChatCompletionToolMessageParam } from 'openai/resources/chat/completions'

import { Message, toolResult } from './messages.js'
import { WorkflowState } from './state.js'
import { Message } from './types.js'
import { Workflow } from './workflow.js'

/**
Expand Down Expand Up @@ -44,11 +44,7 @@ export async function runTools(
messages: context.concat(state.messages),
})

return {
role: 'tool' as const,
tool_call_id: toolCall.id,
content: JSON.stringify(content),
}
return toolResult(toolCall.id, content)
})
)

Expand All @@ -64,11 +60,7 @@ export const addToolResponse = (
if (toolRequestMessage) {
return {
...state,
messages: state.messages.concat({
role: 'tool',
tool_call_id: toolCallId,
content,
}),
messages: [...state.messages, toolResult(toolCallId, content)],
}
}
if (state.children.length > 0) {
Expand Down

0 comments on commit 538d644

Please sign in to comment.