Skip to content

[TypeScript SDK] Add opik-agentica integration package#5430

Draft
vincentkoc wants to merge 19 commits intomainfrom
vincentkoc-code/symbolica-agentica-typescript
Draft

[TypeScript SDK] Add opik-agentica integration package#5430
vincentkoc wants to merge 19 commits intomainfrom
vincentkoc-code/symbolica-agentica-typescript

Conversation

@vincentkoc
Copy link
Member

Details

  • add a new TypeScript integration package: sdks/typescript/src/opik/integrations/opik-agentica
  • implement trackAgentica(...) as a proxy wrapper for Agentica SDK call paths (spawn, agentic, call, and transformation variants)
  • add method/input/output parsers and usage normalization to Opik usage keys (prompt_tokens, completion_tokens, total_tokens)
  • add parser-focused unit tests for input sanitization and usage flattening
  • wire package into repository surfaces:
    • sdks/typescript/README.md
    • sdks/typescript/design/INTEGRATIONS.md
    • sdks/typescript/tsconfig.json excludes
    • .github/workflows/typescript_sdk_integration_publish.yml matrix
    • .github/workflows/release.yaml version bump loop

Change checklist

  • User facing
  • Documentation update

Issues

  • Resolves #
  • OPIK-

Testing

  • cd sdks/typescript/src/opik/integrations/opik-agentica && npm install
  • cd sdks/typescript/src/opik/integrations/opik-agentica && npm run lint
  • cd sdks/typescript/src/opik/integrations/opik-agentica && npm run typecheck
  • cd sdks/typescript/src/opik/integrations/opik-agentica && npm run test
  • cd sdks/typescript/src/opik/integrations/opik-agentica && npm run build

Documentation

  • Updated TypeScript SDK README and integration architecture inventory to include opik-agentica.

@github-actions github-actions bot added documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file Infrastructure typescript *.ts *.tsx TypeScript SDK labels Feb 26, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 26, 2026

📋 PR Linter Failed

Invalid Title Format. Your PR title must include a ticket/issue number and may optionally include component tags ([FE], [BE], etc.).

  • Internal contributors: Open a JIRA ticket and link to it: [OPIK-xxxx] or [CUST-xxxx] or [DND-xxxx] or [DEV-xxxx] [COMPONENT] Your change
  • External contributors: Open a Github Issue and link to it via its number: [issue-xxxx] [COMPONENT] Your change
  • No ticket: Use [NA] [COMPONENT] Your change (Issues section not required)

Example: [issue-3108] [BE] [FE] Fix authentication bug or [OPIK-1234] Fix bug or [NA] Update README


Incomplete Issues Section. You must reference at least one GitHub issue (#xxxx), Jira ticket (OPIK-xxxx), CUST ticket (CUST-xxxx), DEV ticket (DEV-xxxx), or DND ticket (DND-xxxx) under the ## Issues section.

Comment on lines +29 to +60
const attachUsageCollectorToArgs = (
methodName: string,
args: unknown[],
onUsage: (usage: Record<string, number> | undefined) => void
): unknown[] => {
if (methodName !== "agentic" && methodName !== "agenticTransformation") {
return args;
}

const nextArgs = [...args];
const configIndex = typeof nextArgs[1] === "object" && nextArgs[1] !== null ? 2 : 1;
const currentConfig = nextArgs[configIndex];

if (typeof currentConfig === "object" && currentConfig !== null) {
const configObject = currentConfig as Record<string, unknown>;
const previousOnUsage = configObject.onUsage;

nextArgs[configIndex] = {
...configObject,
onUsage: (usage: unknown) => {
onUsage(parseUsage(usage));
if (typeof previousOnUsage === "function") {
(previousOnUsage as (value: unknown) => void)(usage);
}
},
};
return nextArgs;
}

nextArgs[configIndex] = {
onUsage: (usage: unknown) => onUsage(parseUsage(usage)),
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

configIndex assumes args[1] is a scope when it is present, so (prompt, config) calls detect an object at args[1] and set configIndex = 2; the existing config remains at index 1 and onUsage is written to a new argument slot, leaving the real config without the hook and preventing usage events from ever firing for that signature. Can we detect the (prompt, config) signature and inject onUsage into index 1 instead?

Finding type: Logical Bugs


Want Baz to fix this for you? Activate Fixer

Other fix methods

Fix in Cursor

Prompt for AI Agents:

In sdks/typescript/src/opik/integrations/opik-agentica/src/decorators.ts around lines 29
to 60, the attachUsageCollectorToArgs function incorrectly sets configIndex = 2 whenever
args[1] is an object, which breaks the (prompt, config) signature by placing the onUsage
hook at the wrong slot. Change the configIndex detection so that if args[0] is a prompt
(e.g., a string) and args[1] is an object, set configIndex = 1; otherwise if args[2] is
an object set configIndex = 2; otherwise default to 1. Apply this conditional
assignment, keep the rest of the onUsage injection logic intact, and ensure existing
behavior for other signatures is preserved.

Comment on lines +1 to +11
import { Opik, OpikConfig } from "opik";

export class OpikSingleton {
private static instance: Opik | null = null;

public static getInstance(params?: Partial<OpikConfig>): Opik {
if (!OpikSingleton.instance) {
OpikSingleton.instance = new Opik(params);
}
return OpikSingleton.instance;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OpikSingleton here is verbatim to the singleton in opik-openai/src/singleton.ts and opik-gemini/src/singleton.ts, so every change has to be copied across integrations; can we extract a shared OpikSingleton module under src/opik/integrations/shared and import it instead of duplicating?

Finding type: Code Dedup and Conventions


Want Baz to fix this for you? Activate Fixer

Comment on lines +66 to +70
const config: TracingConfig = {
...opikConfig,
generationName: createGenerationName(
sdk,
methodName,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

config.generationName is always set to createGenerationName(..., opikConfig?.generationName) before any method call, and the same config (with that explicit name) is passed down when wrapping nested results (trackAgentica(..., config, cache)); nested agents therefore never recompute the method-specific name and every span inherits the parent property's generationName (e.g. Agentica.spawn for agent.call). Can we avoid carrying the parent generationName into recursive wraps so each method can use its own default name?

Finding type: Logical Bugs


Want Baz to fix this for you? Activate Fixer

Other fix methods

Fix in Cursor

Prompt for AI Agents:

In sdks/typescript/src/opik/integrations/opik-agentica/src/trackAgentica.ts around lines
66 to 70 (the Proxy get handler / traced-method logic), the current code builds a
per-method TracingConfig (with generationName) and then passes that same config into
recursive wrapping, causing nested agents to inherit the parent's generationName.
Refactor so recursive wraps do not receive the per-method TracingConfig: change the
calls that currently pass 'config' into trackAgentica and wrapResultIfNeeded (the
Promise.then branch and the synchronous wrap return, and the branch that wraps nested
object properties) to pass the original TrackOpikConfig parameter 'opikConfig' (or a
shallow copy with generationName unset) instead. This ensures nested proxies recompute
their own generationName per method rather than inheriting the parent one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation Infrastructure TypeScript SDK typescript *.ts *.tsx

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant