diff --git a/openai-agents/src/agent-patterns/workflows.ts b/openai-agents/src/agent-patterns/workflows.ts index 609cc0a58..69840cfaf 100644 --- a/openai-agents/src/agent-patterns/workflows.ts +++ b/openai-agents/src/agent-patterns/workflows.ts @@ -99,6 +99,7 @@ export async function llmAsJudge(prompt: string): Promise { return output; } +// @@@SNIPSTART typescript-openai-agents-agent-as-tool-workflow export async function agentsAsTools(prompt: string): Promise { const specialistAgent = new Agent({ name: 'SpecialistAgent', @@ -121,6 +122,7 @@ export async function agentsAsTools(prompt: string): Promise { const result = await runner.run(orchestratorAgent, prompt); return result.finalOutput ?? ''; } +// @@@SNIPEND export async function inputGuardrail(prompt: string): Promise { const blockedKeywords = ['blocked', 'BLOCK', 'forbidden']; diff --git a/openai-agents/src/basic/client.ts b/openai-agents/src/basic/client.ts index 03f8e640c..ab0a857cc 100644 --- a/openai-agents/src/basic/client.ts +++ b/openai-agents/src/basic/client.ts @@ -22,6 +22,7 @@ async function run() { const scenario = process.argv[2] ?? 'hello-world'; console.log(`Running scenario: ${scenario}`); + // @@@SNIPSTART typescript-openai-agents-hello-world-client const connection = await Connection.connect(); const client = new Client({ connection, @@ -30,6 +31,7 @@ async function run() { const taskQueue = 'openai-agents-basic'; const workflowId = 'openai-agents-' + nanoid(); + // @@@SNIPEND let handle; switch (scenario) { diff --git a/openai-agents/src/basic/worker.ts b/openai-agents/src/basic/worker.ts index d90f53275..1110b22c8 100644 --- a/openai-agents/src/basic/worker.ts +++ b/openai-agents/src/basic/worker.ts @@ -11,6 +11,7 @@ async function run() { const connection = await NativeConnection.connect({ address: 'localhost:7233' }); try { + // @@@SNIPSTART typescript-openai-agents-hello-world-worker const worker = await Worker.create({ connection, taskQueue: 'openai-agents-basic', @@ -33,6 +34,7 @@ async function run() { }, }); await worker.run(); + // @@@SNIPEND } finally { await connection.close(); } diff --git a/openai-agents/src/basic/workflows.ts b/openai-agents/src/basic/workflows.ts index 45f6aef0e..aa7396904 100644 --- a/openai-agents/src/basic/workflows.ts +++ b/openai-agents/src/basic/workflows.ts @@ -6,12 +6,15 @@ import type * as activities from './activities'; const localActivities = proxyLocalActivities({ startToCloseTimeout: '10 seconds' }); +// @@@SNIPSTART typescript-openai-agents-hello-world-workflow export async function helloWorld(prompt: string): Promise { const agent = new Agent({ name: 'HelloAgent', instructions: 'You are a helpful assistant.' }); const result = await new TemporalOpenAIRunner().run(agent, prompt); return result.finalOutput ?? ''; } +// @@@SNIPEND +// @@@SNIPSTART typescript-openai-agents-activity-tool-workflow export async function tools(prompt: string): Promise { const weatherTool = activityAsTool( { @@ -35,7 +38,9 @@ export async function tools(prompt: string): Promise { const result = await new TemporalOpenAIRunner().run(agent, prompt); return result.finalOutput ?? ''; } +// @@@SNIPEND +// @@@SNIPSTART typescript-openai-agents-inline-tool-workflow export async function inlineTool(prompt: string): Promise { const addTool = tool({ name: 'add', @@ -52,6 +57,7 @@ export async function inlineTool(prompt: string): Promise { const result = await new TemporalOpenAIRunner().run(agent, prompt); return result.finalOutput ?? ''; } +// @@@SNIPEND export async function localActivityTool(prompt: string): Promise { const headlinesTool = tool({ diff --git a/openai-agents/src/human-approval/workflows.ts b/openai-agents/src/human-approval/workflows.ts index 1bee9b39f..630450bb7 100644 --- a/openai-agents/src/human-approval/workflows.ts +++ b/openai-agents/src/human-approval/workflows.ts @@ -8,6 +8,7 @@ export interface ApprovalInput { resumeFromRunState?: string; } +// @@@SNIPSTART typescript-openai-agents-approval-workflow export async function approvalWorkflow(input: ApprovalInput = {}): Promise { const action = tool({ name: 'dangerousAction', @@ -57,3 +58,4 @@ export async function approvalWorkflow(input: ApprovalInput = {}): Promise({ resumeFromRunState: result.state.toString() }); throw new Error('unreachable'); } +// @@@SNIPEND diff --git a/openai-agents/src/mcp/worker.ts b/openai-agents/src/mcp/worker.ts index 5ede31a92..a71bd090d 100644 --- a/openai-agents/src/mcp/worker.ts +++ b/openai-agents/src/mcp/worker.ts @@ -26,6 +26,29 @@ async function run() { const filesystemServerPath = path.resolve(__dirname, 'servers', 'filesystem-server.ts'); + // @@@SNIPSTART typescript-openai-agents-mcp-worker + // A stateless provider reconnects per operation, so each tool call stands alone. + const statelessProviders = [ + new StatelessMCPServerProvider( + 'filesystem', + () => + new MCPServerStdio({ + command: 'npx', + args: ['ts-node', filesystemServerPath], + name: 'filesystem', + }), + ), + new StatelessMCPServerProvider( + 'streamableHttp', + () => new MCPServerStreamableHttp({ url: toolsHttp.url, name: 'streamableHttp' }), + ), + new StatelessMCPServerProvider('sse', () => new MCPServerSSE({ url: toolsSse.url, name: 'sse' })), + ]; + + // A stateful provider also takes the connection, which the plugin uses to run a + // dedicated Worker holding the MCP session open for the life of the Workflow run. + const statefulProviders = [new StatefulMCPServerProvider('memory', () => createNotesServer(), connection)]; + const worker = await Worker.create({ connection, taskQueue: 'openai-agents-mcp', @@ -35,25 +58,10 @@ async function run() { new OpenAIAgentsPlugin({ modelProvider: new OpenAIProvider({ apiKey }), modelParams: { useLocalActivity: true }, - mcpServerProviders: [ - new StatelessMCPServerProvider( - 'filesystem', - () => - new MCPServerStdio({ - command: 'npx', - args: ['ts-node', filesystemServerPath], - name: 'filesystem', - }), - ), - new StatelessMCPServerProvider( - 'streamableHttp', - () => new MCPServerStreamableHttp({ url: toolsHttp.url, name: 'streamableHttp' }), - ), - new StatelessMCPServerProvider('sse', () => new MCPServerSSE({ url: toolsSse.url, name: 'sse' })), - new StatefulMCPServerProvider('memory', () => createNotesServer(), connection), - ], + mcpServerProviders: [...statelessProviders, ...statefulProviders], }), ], + // @@@SNIPEND bundlerOptions: { webpackConfigHook: (config) => ({ ...config, diff --git a/openai-agents/src/mcp/workflows.ts b/openai-agents/src/mcp/workflows.ts index 81afc803d..b74143435 100644 --- a/openai-agents/src/mcp/workflows.ts +++ b/openai-agents/src/mcp/workflows.ts @@ -5,6 +5,7 @@ import type { Activities } from './activities'; const activities = proxyActivities({ startToCloseTimeout: '1 minute' }); +// @@@SNIPSTART typescript-openai-agents-stateless-mcp-workflow export async function filesystem(prompt: string): Promise { const agent = new Agent({ name: 'FilesystemAgent', @@ -14,6 +15,7 @@ export async function filesystem(prompt: string): Promise { const result = await new TemporalOpenAIRunner().run(agent, prompt); return result.finalOutput ?? ''; } +// @@@SNIPEND export async function streamableHttp(prompt: string): Promise { const agent = new Agent({ @@ -45,6 +47,7 @@ export async function promptServer(prompt: string): Promise { return result.finalOutput ?? ''; } +// @@@SNIPSTART typescript-openai-agents-stateful-mcp-workflow export async function statefulMemory(prompt: string): Promise { const server = statefulMcpServer('memory'); await server.connect(); @@ -60,3 +63,4 @@ export async function statefulMemory(prompt: string): Promise { await server.cleanup(); } } +// @@@SNIPEND diff --git a/openai-agents/src/nexus-tools/api.ts b/openai-agents/src/nexus-tools/api.ts index 87c172899..d26708207 100644 --- a/openai-agents/src/nexus-tools/api.ts +++ b/openai-agents/src/nexus-tools/api.ts @@ -1,5 +1,6 @@ import * as nexus from 'nexus-rpc'; +// @@@SNIPSTART typescript-openai-agents-nexus-tools-api export interface GetWeatherInput { city: string; } @@ -13,3 +14,4 @@ export interface GetWeatherOutput { export const weatherService = nexus.service('weather', { getWeather: nexus.operation(), }); +// @@@SNIPEND diff --git a/openai-agents/src/nexus-tools/workflows.ts b/openai-agents/src/nexus-tools/workflows.ts index a0efa1916..a9a6c6c07 100644 --- a/openai-agents/src/nexus-tools/workflows.ts +++ b/openai-agents/src/nexus-tools/workflows.ts @@ -4,6 +4,7 @@ import { weatherService } from './api'; export const WEATHER_ENDPOINT = 'openai-agents-weather-endpoint'; +// @@@SNIPSTART typescript-openai-agents-nexus-tool-workflow export async function nexusToolWorkflow(prompt: string): Promise { const weatherTool = nexusOperationAsTool( weatherService.operations.getWeather, @@ -29,3 +30,4 @@ export async function nexusToolWorkflow(prompt: string): Promise { const result = await new TemporalOpenAIRunner().run(agent, prompt); return result.finalOutput ?? ''; } +// @@@SNIPEND diff --git a/openai-agents/src/sessions/workflows.ts b/openai-agents/src/sessions/workflows.ts index 67bf86ff8..51ac54464 100644 --- a/openai-agents/src/sessions/workflows.ts +++ b/openai-agents/src/sessions/workflows.ts @@ -3,6 +3,7 @@ import type { AgentInputItem } from '@openai/agents-core'; import { TemporalOpenAIRunner, WorkflowSafeMemorySession } from '@temporalio/openai-agents/workflow'; import { continueAsNew } from '@temporalio/workflow'; +// @@@SNIPSTART typescript-openai-agents-session-workflow export async function multiTurnChat(prompts: string[]): Promise { const agent = new Agent({ name: 'ChatAgent', instructions: 'You are a helpful assistant.' }); const session = new WorkflowSafeMemorySession(); @@ -14,6 +15,7 @@ export async function multiTurnChat(prompts: string[]): Promise { } return replies; } +// @@@SNIPEND export interface CarryoverChatInput { prompts: string[]; @@ -21,6 +23,7 @@ export interface CarryoverChatInput { accumulated?: string[]; } +// @@@SNIPSTART typescript-openai-agents-session-carryover-workflow export async function carryoverChat(input: CarryoverChatInput): Promise { const agent = new Agent({ name: 'ChatAgent', instructions: 'You are a helpful assistant.' }); const session = new WorkflowSafeMemorySession({ initialItems: input.initialItems }); @@ -46,3 +49,4 @@ export async function carryoverChat(input: CarryoverChatInput): Promise { const agent = new Agent({ name: 'WebSearchAgent', @@ -11,6 +12,7 @@ export async function webSearch(prompt: string): Promise { const result = await new TemporalOpenAIRunner().run(agent, prompt); return result.finalOutput ?? ''; } +// @@@SNIPEND export async function imageGeneration(prompt: string): Promise { const agent = new Agent({ diff --git a/openai-agents/src/tracing/worker.ts b/openai-agents/src/tracing/worker.ts index aa7896da0..fc68951b3 100644 --- a/openai-agents/src/tracing/worker.ts +++ b/openai-agents/src/tracing/worker.ts @@ -40,6 +40,7 @@ async function run() { connection, taskQueue: 'openai-agents-tracing', workflowsPath: require.resolve('./workflows'), + // @@@SNIPSTART typescript-openai-agents-tracing-worker plugins: [ new OpenAIAgentsPlugin({ modelProvider: new OpenAIProvider({ apiKey }), @@ -47,6 +48,7 @@ async function run() { interceptorOptions: { useOtelInstrumentation, addTemporalSpans: true }, }), ], + // @@@SNIPEND bundlerOptions: { webpackConfigHook: (config) => ({ ...config,