Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b1afe2e
feat(agents): Initial implementation.
Murike Feb 27, 2026
906a007
feat(agents): Tests for initial implementation.
Murike Feb 27, 2026
9016c3a
feat(agents): Rebased to integrate new sdkLogger.
Murike Feb 27, 2026
3b8995e
fix(agents): Updated galileo-generated version, fixed parseUsage read…
Murike Mar 4, 2026
b69d4ae
fix(agents): Added support to log orphaned spans on onSpanStart.
Murike Mar 4, 2026
a82115a
fix(agents): Improved support for agent spans.
Murike Mar 5, 2026
98eb59c
fix(agents): firstInput refactored to feed first input data to span.
Murike Mar 5, 2026
2aa8c8b
fix(agents): Moved import test to inside constructor, to avoid promti…
Murike Mar 5, 2026
ed8290a
fix(agents): Added processing of custom galileoSpan.
Murike Mar 5, 2026
a0f4289
fix(agents): Added statusCode propagation for workflow spans.
Murike Mar 5, 2026
cf052ca
feature(examples): Added example for openai-agents.
Murike Mar 12, 2026
1597d88
fix(workflow): Refactored agent span processing to mirror current Pyt…
Murike Mar 17, 2026
feb0a11
feature(serialization): Updated serialization method (to toStringReco…
Murike Mar 18, 2026
d5ca5e1
feature(serialization): Centralized serialization of input and output…
Murike Mar 18, 2026
10d99df
fix(tools): Assigning tools to correct field, instead of serializing …
Murike Mar 18, 2026
c14a126
feat(custom): Refactored process to manage custom (galileo) spans, fo…
Murike Mar 19, 2026
151496d
feat(error): Refactored support for status code and error.
Murike Mar 19, 2026
e55124b
feat(error): Refactored support for status code and error (2).
Murike Mar 20, 2026
0555828
feat(lastOutput): Adjusted rule to recover value for _lastOutput.
Murike Mar 20, 2026
02d0965
chore(organization): Refactored code adding new functions for reusing…
Murike Mar 20, 2026
f8111bc
chore(parseUsage): Refactored repeated use of parseUsage.
Murike Mar 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions examples/openai/agents.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import dotenv from 'dotenv';
import { z } from 'zod';
import { Agent, run } from '@openai/agents';
import {
init,
flush,
registerGalileoTraceProcessor
} from '../../dist/index.js';

dotenv.config();

await init({
projectName: 'openai-agents-example'
});

await registerGalileoTraceProcessor();

const triageAgent = new Agent({
name: 'Triage Agent',
instructions:
'You determine which agent should handle the user request. ' +
'If the question is about weather, hand off to the Weather Agent. ' +
'Otherwise, answer the question yourself.',
handoffs: [] // populated below after declaring weatherAgent
});

const weatherAgent = new Agent({
name: 'Weather Agent',
instructions:
'You provide weather information. ' +
'Given a city name, respond with a short, friendly weather summary. ' +
'Make up plausible weather data for demonstration purposes.',
tools: [
tool({
name: 'get_weather',
description: 'Get the current weather for a city',
parameters: z.object({
city: z.string().describe('The city to get weather for')
}),
execute: async (params) => {
const { city } = params;
const temps = { london: 14, tokyo: 22, 'new york': 18, paris: 16 };
const temp =
temps[city.toLowerCase()] ?? Math.floor(Math.random() * 30);
return JSON.stringify({
city,
temperature_c: temp,
condition: temp > 20 ? 'Sunny' : 'Partly cloudy'
});
}
})
]
});

triageAgent.handoffs.push(weatherAgent);

async function main() {
console.log('=== OpenAI Agents SDK + Galileo Tracing ===\n');

console.log('--- Simple single-agent run ---');
const simpleResult = await run(triageAgent, 'What is 2 + 2?');
console.log('Response:', simpleResult.finalOutput, '\n');

console.log('--- Handoff + tool call run ---');
const weatherResult = await run(triageAgent, "What's the weather in Tokyo?");
console.log('Response:', weatherResult.finalOutput, '\n');

await flush();
console.log('Done — traces flushed to Galileo.');
}

main().catch((err) => {
console.error('Unhandled error:', err);
process.exit(1);
});
5 changes: 4 additions & 1 deletion examples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@
"@langchain/community": "^0.3.18",
"@langchain/core": "^0.3.13",
"@langchain/openai": "^0.3.11",
"@openai/agents": "^0.7.0",
"@rungalileo/galileo": "file:..",
"dotenv": "^16.4.5",
"typecript": "^0.0.1-security"
"openai": "^6.26.0",
"typecript": "^0.0.1-security",
"zod": "^4.0.0"
}
}
21 changes: 12 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@
"@langchain/openai": "^0.3.11",
"tiktoken": "^1.0.13"
},
"peerDependencies": {
"@openai/agents": ">=0.4.0",
"openai": ">=4.0.0"
},
"peerDependenciesMeta": {
"@openai/agents": {
"optional": true
},
"openai": {
"optional": true
}
},
"devDependencies": {
"@hey-api/openapi-ts": "^0.88.0",
"@types/jest": "^29.5.14",
Expand Down
12 changes: 6 additions & 6 deletions src/handlers/langchain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { AgentFinish } from '@langchain/core/agents';
import { Document, DocumentInterface } from '@langchain/core/documents';
import { GalileoSingleton } from '../singleton';
import { GalileoLogger } from '../utils/galileo-logger';
import { toStringValue, convertToStringDict } from '../utils/serialization';
import { toStringValue, toStringRecord } from '../utils/serialization';
import { getSdkLogger } from 'galileo-generated';
import { Serialized } from '@langchain/core/load/serializable.js';

Expand Down Expand Up @@ -151,8 +151,8 @@ export class GalileoCallback
let metadata: Record<string, string> | undefined = undefined;
if (node.spanParams.metadata) {
try {
metadata = convertToStringDict(
node.spanParams.metadata as Record<string, any>
metadata = toStringRecord(
node.spanParams.metadata as Record<string, unknown>
);
} catch (e) {
sdkLogger.warn('Unable to convert metadata to a string dictionary', e);
Expand Down Expand Up @@ -485,7 +485,7 @@ export class GalileoCallback
| undefined;

// Serialize messages safely
let serializedMessages;
let serializedMessages: unknown;
try {
const flattenedMessages = messages.flat().map((msg) => ({
content: msg.content,
Expand Down Expand Up @@ -516,7 +516,7 @@ export class GalileoCallback
public async handleLLMEnd(output: LLMResult, runId: string): Promise<void> {
const tokenUsage = output.llmOutput?.tokenUsage || {};

let serializedOutput;
let serializedOutput: unknown;
try {
const flattenedOutput = output.generations.flat().map((g) => ({
text: g.text,
Expand Down Expand Up @@ -605,7 +605,7 @@ export class GalileoCallback
documents: DocumentInterface<Record<string, unknown>>[],
runId: string
): Promise<void> {
let serializedResponse;
let serializedResponse: unknown;
try {
serializedResponse = documents.map((doc) => ({
pageContent: doc.pageContent,
Expand Down
74 changes: 74 additions & 0 deletions src/handlers/openai-agents/custom-span.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

/**
* Duck-typed interface describing the expected shape of a Galileo span object
* that can be injected into the OpenAI Agents tracing flow.
*
* Mirrors the fields extracted by galileo-python's GalileoCustomSpan handler:
* input, output, metadata (user_metadata), tags, status_code, and type.
*/
export interface GalileoSpanLike {
type?: string;
input?: unknown;
output?: unknown;
name?: string;
metadata?: Record<string, string>;
tags?: string[];
statusCode?: number;
}

/**
* A lightweight subtype of CustomSpanData that carries a reference to a
* pre-configured GalileoSpan so it can be injected into the agent tracing flow.
*
* The __galileoCustom flag is used by mapSpanType() to distinguish this from
* ordinary CustomSpanData objects.
*/
export interface GalileoCustomSpanData {
/** Always 'custom' to satisfy the SDK's SpanData union discriminant. */
type: 'custom';
/** (Optional) Display name for the span. */
name?: string;
/** Arbitrary data payload, must contain a 'galileoSpan' key with the GalileoSpan reference. */
data: Record<string, unknown> & { galileoSpan: GalileoSpanLike };
/** Sentinel flag used internally by mapSpanType() to identify this type. */
__galileoCustom: true;
}

/**
* Creates a GalileoCustomSpanData object that wraps an existing Galileo span.
* @param galileoSpan - The Galileo span object to embed.
* @param name - (Optional) Display name for the span.
* @param extraData - (Optional) Additional data to include in the span data payload.
* @returns A GalileoCustomSpanData object.
*/
export function createGalileoCustomSpanData(
galileoSpan: GalileoSpanLike,
name?: string,
extraData?: Record<string, unknown>
): GalileoCustomSpanData {
return {
type: 'custom',
name,
data: {
...extraData,
galileoSpan
},
__galileoCustom: true
};
}

/**
* Type guard that checks whether a span data object is a GalileoCustomSpanData.
* @param spanData - The span data to check.
* @returns True if the span data is a GalileoCustomSpanData.
*/
export function isGalileoCustomSpanData(
spanData: unknown
): spanData is GalileoCustomSpanData {
return (
typeof spanData === 'object' &&
spanData !== null &&
(spanData as any).__galileoCustom === true
);
}
Loading
Loading