-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit introduces a new `Swarm` module that includes REPL functionality and tools for handling functions. Additional changes include the integration of the `chalk` library for colorful terminal output. Modifications are made to `package.json` to reflect these new dependencies and module structures.
- Loading branch information
1 parent
9a26060
commit 93b038d
Showing
8 changed files
with
501 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export { runSwarmRepl } from './repl.js'; | ||
export { Swarm } from './swarm.js'; | ||
export { swarmFunction, swarmHandoff } from './swarm-tools.js'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import * as readline from 'node:readline'; | ||
|
||
import chalk from 'chalk'; | ||
|
||
import { type Msg, MsgUtil } from '../model/index.js'; | ||
import { Swarm } from './swarm.js'; | ||
import { type Agent } from './types.js'; | ||
|
||
function prettyPrintMessages(messages: Msg[]): void { | ||
for (const message of messages) { | ||
// Print tool results | ||
if (MsgUtil.isToolResult(message)) { | ||
const { content } = message; | ||
console.log(`<== ${chalk.green(content)}`); | ||
} | ||
|
||
if (message.role !== 'assistant') continue; | ||
|
||
// Print agent name in blue | ||
if ('name' in message) { | ||
process.stdout.write(`${chalk.blue(message.name || '')}: `); | ||
} | ||
|
||
// Print response, if any | ||
if (message.content) { | ||
console.log(message.content); | ||
} | ||
|
||
// Print tool calls in purple, if any | ||
const toolCalls = MsgUtil.isToolCall(message) ? message.tool_calls : []; | ||
if (toolCalls.length > 1) { | ||
console.log(); | ||
} | ||
for (const toolCall of toolCalls) { | ||
const { name, arguments: args } = toolCall.function; | ||
const argObj = JSON.parse(args); | ||
const argStr = JSON.stringify(argObj).replace(/:/g, '='); | ||
if (name.startsWith('transfer_')) { | ||
console.log(`<> ${chalk.yellow(name)}(${argStr.slice(1, -1)})`); | ||
} else { | ||
console.log(`${chalk.magenta(name)}(${argStr.slice(1, -1)})`); | ||
} | ||
} | ||
} | ||
} | ||
|
||
export async function runSwarmRepl( | ||
startingAgent: Agent, | ||
contextVariables: Record<string, any> = {} | ||
): Promise<void> { | ||
const client = new Swarm(); | ||
console.log('Swarm initialized.'); | ||
|
||
let messages: Msg[] = []; | ||
let agent = startingAgent; | ||
|
||
const rl = readline.createInterface({ | ||
input: process.stdin, | ||
output: process.stdout, | ||
}); | ||
|
||
while (true) { | ||
const userInput = await new Promise<string>((resolve) => { | ||
rl.question(`${chalk.gray('User')}: `, resolve); | ||
}); | ||
|
||
messages.push({ role: 'user', content: userInput }); | ||
|
||
const response = await client.run({ | ||
agent, | ||
messages, | ||
ctx: contextVariables, | ||
}); | ||
|
||
prettyPrintMessages(response.messages); | ||
messages = messages.concat(response.messages); | ||
agent = response.agent; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import { z } from 'zod'; | ||
|
||
import { | ||
extractZodObject, | ||
type Msg, | ||
MsgUtil, | ||
zodToJsonSchema, | ||
} from '../model/index.js'; | ||
import { getErrorMsg } from '../model/utils/errors.js'; | ||
import { cleanString } from '../model/utils/message-util.js'; | ||
import { type Agent, type SwarmFunc } from './types.js'; | ||
|
||
export function swarmFunction<Schema extends z.ZodObject<any>, Return>( | ||
spec: { | ||
/** Name of the function. */ | ||
name: string; | ||
/** Description of the function. */ | ||
description?: string; | ||
/** Zod schema for the arguments string. */ | ||
argsSchema: Schema; | ||
}, | ||
/** Implementation of the function to call with the parsed arguments. */ | ||
implementation: (params: z.infer<Schema>) => Promise<Return> | ||
): SwarmFunc { | ||
/** Parse the arguments string, optionally reading from a message. */ | ||
const parseArgs = (input: string | Msg) => { | ||
if (typeof input === 'string') { | ||
return extractZodObject({ schema: spec.argsSchema, json: input }); | ||
} else if (MsgUtil.isFuncCall(input)) { | ||
const args = input.function_call.arguments; | ||
return extractZodObject({ schema: spec.argsSchema, json: args }); | ||
} else { | ||
throw new Error('Invalid input type'); | ||
} | ||
}; | ||
|
||
// Call the implementation function with the parsed arguments. | ||
const aiFunction = async (input: string | Msg) => { | ||
const parsedArgs = parseArgs(input); | ||
const result = await implementation(parsedArgs); | ||
try { | ||
const resultStr = | ||
typeof result === 'string' ? result : JSON.stringify(result); | ||
return { value: resultStr }; | ||
} catch (err) { | ||
console.error(`Error stringifying function ${spec.name} result:`, err); | ||
const errMsg = getErrorMsg(err); | ||
return { | ||
value: `Error stringifying function ${spec.name} result: ${errMsg}`, | ||
}; | ||
} | ||
}; | ||
|
||
aiFunction.parseArgs = parseArgs; | ||
aiFunction.argsSchema = spec.argsSchema; | ||
aiFunction.spec = { | ||
name: spec.name, | ||
description: cleanString(spec.description ?? ''), | ||
parameters: zodToJsonSchema(spec.argsSchema), | ||
}; | ||
|
||
return aiFunction; | ||
} | ||
|
||
/** This is a simple no-op function that can be used to transfer context to another agent. */ | ||
export function swarmHandoff(args: { | ||
agent: Agent; | ||
description?: string; | ||
}): SwarmFunc { | ||
const { agent, description } = args; | ||
const schema = z.object({}); | ||
|
||
/** Parse the arguments string, optionally reading from a message. */ | ||
const parseArgs = (input: string | Msg) => { | ||
if (typeof input === 'string') { | ||
return extractZodObject({ schema, json: input }); | ||
} else if (MsgUtil.isFuncCall(input)) { | ||
const args = input.function_call.arguments; | ||
return extractZodObject({ schema, json: args }); | ||
} else { | ||
throw new Error(`Invalid input type`); | ||
} | ||
}; | ||
const aiFunction = async () => { | ||
const value = `Transfered to ${agent.name}. Adopt the role and responsibilities of ${agent.name} and continue the conversation.`; | ||
return { value, agent }; | ||
}; | ||
|
||
aiFunction.parseArgs = parseArgs; | ||
aiFunction.argsSchema = schema; | ||
aiFunction.spec = { | ||
name: `transfer_to_${agent.name}`, | ||
description: description || '', | ||
parameters: zodToJsonSchema(schema), | ||
}; | ||
|
||
return aiFunction; | ||
} |
Oops, something went wrong.