Skip to content

Conversation

zubairirfan96
Copy link
Collaborator

@zubairirfan96 zubairirfan96 commented Aug 15, 2025

Summary by CodeRabbit

  • New Features
    • Post-processing for function call results to normalize tool outputs.
    • Tool outputs may include call IDs for better traceability.
  • Improvements
    • More reliable streaming with safer, typed event handling.
    • Broader tool definition support and consistent tool-choice handling.
    • Simplified, model-based search cost reporting.
  • Tests
    • Updated unit tests to reflect new tool transformation outputs.
  • Chores
    • Version bumps in core (1.5.45) and SDK (1.0.43).

Copy link

coderabbitai bot commented Aug 15, 2025

Walkthrough

This PR bumps package versions and refactors OpenAI integration internals. It consolidates prompt token counting, overhauls Responses API streaming and tool handling, simplifies search tool cost constants, adjusts related types (adding callId), and updates unit tests accordingly. Public API adds processFunctionCallResults and modifies transformToolsConfig output and input shapes.

Changes

Cohort / File(s) Summary
Version bumps
packages/core/package.json, packages/sdk/package.json
Patch version increments: core 1.5.44→1.5.45; sdk 1.0.42→1.0.43.
OpenAI connector prompt tokens
packages/core/src/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.ts
Introduces computePromptTokens helper; unifies token counting for request and streamRequest; vision-aware; error-safe.
Responses API refactor
.../openai/apiInterfaces/ResponsesApiInterface.ts
Typed event routing with dedicated handlers; improved tool data management; tool/message transformation overhaul; tool-choice validation; cost/usage refactor; new public method processFunctionCallResults.
Search tool cost constants
.../openai/apiInterfaces/constants.ts
Replaces typed hierarchical SEARCH_TOOL_COSTS with flat model-to-cost map; removes type annotation/import.
Type updates (OpenAI)
.../openai/types.ts
Removes public interface SearchToolCostConfig.
Type updates (LLM ToolData)
packages/core/src/types/LLM.types.ts
Adds optional callId?: string to ToolData.
Unit tests (Responses API tools)
packages/core/tests/unit/openai/ResponsesApiInterface.unit.test.ts
Updates expectations for transformToolsConfig: removes modelInfo input, adds strict: false in outputs.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ResponsesAPI
  participant EventRouter as Event Handlers
  participant ToolsData as Tools Store

  Client->>ResponsesAPI: stream()
  loop Stream events
    ResponsesAPI->>EventRouter: route(event)
    alt web_search events
      EventRouter->>ToolsData: upsert web_search state (immutable)
    else function_call delta/done
      EventRouter->>ToolsData: update function args/results
    else output text/item
      EventRouter->>ToolsData: append output fragments
    end
    EventRouter-->>ResponsesAPI: finish status?
  end
  ResponsesAPI-->>Client: final output + tools data
Loading
sequenceDiagram
  participant Caller
  participant RespAPI as ResponsesAPI
  participant Transformer as Tool/Message Transformer

  Caller->>RespAPI: request(messages, toolsConfig, toolChoice)
  RespAPI->>Transformer: applyToolMessageTransformation(messages)
  RespAPI->>Transformer: transformToolsConfig(toolsConfig)
  Transformer-->>RespAPI: normalized messages + tools (strict flag)
  RespAPI->>RespAPI: validateToolChoice()
  RespAPI-->>Caller: prepared request payload
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

I twitch my ears at version’s climb,
New streams now sing in tidy rhyme.
Tools march in strict but friendly pairs,
Costs slim down with lighter fares.
A hop, a bump, a callId found—
I thump my paws: robust and sound. 🐇✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🔭 Outside diff range comments (1)
packages/core/src/types/LLM.types.ts (1)

285-297: ToolData.callId — propagation verified; please add clarifying comments

Quick summary: ResponsesApiInterface already sets/preserves callId and uses it when emitting tool events, so propagation is implemented end-to-end. Please add a short comment clarifying semantics of id vs callId to avoid future confusion.

Files/locations to note

  • packages/core/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts
    • process/stream handlers extract item.call_id and set/preserve tool.callId (e.g. creation at ~lines 381–396).
    • Mapping/emits use callId when available:
      • id: tool.callId || tool.id and callId: tool.callId (~lines 226–229)
      • emitter.emit('tool_call_started', { id: callId, ... }) (~line 416)
      • emitter.emit('tool_call_progress', { id: entry.callId || itemId, ... }) (~line 462)
      • emitter.emit('tool_call_completed', { id: updatedEntry.callId || itemId, ... }) (~line 493)
  • packages/core/src/types/LLM.types.ts
    • ToolData includes callId?: string; add explicit comments describing the difference between id and callId.
  • packages/core/src/subsystems/IO/Log.service/ (LogConnector.ts, ConsoleLog.class.ts)
    • These APIs also accept a callId parameter for logging — confirm this uses the same convention or rename to avoid ambiguity.

Suggested minimal ToolData update (inline comment clarification)

export type ToolData = {
  index: number;
  id: string; // Chat Completions tool-call identifier (item.id)
  type: string;
  name: string;
  arguments: string | Record<string, any>;
  role: 'user' | 'tool' | 'assistant';
  result?: string;
  function?: any;
  error?: string; // for Bedrock
  callId?: string; // OpenAI Responses API call_id (streaming mapping); prefer callId for Responses API flows
};

Category:

🧹 Nitpick comments (12)
packages/core/src/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.ts (2)

86-90: Good consolidation of token counting; remove unused lastMessage.

lastMessage is computed but not used after introducing computePromptTokens. Clean it up to avoid noise.

Apply this diff:

-        const messages = _body?.messages || [];
-        const lastMessage = messages[messages.length - 1];
-        const promptTokens = await this.computePromptTokens(messages, context);
+        const messages = _body?.messages || [];
+        const promptTokens = await this.computePromptTokens(messages, context);

143-147: Same here: remove unused lastMessage in streamRequest.

Apply this diff:

-        const messages = body?.messages || body?.input || [];
-        const lastMessage = messages[messages.length - 1];
-        const promptTokens = await this.computePromptTokens(messages, context);
+        const messages = body?.messages || body?.input || [];
+        const promptTokens = await this.computePromptTokens(messages, context);
packages/core/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/constants.ts (1)

10-13: Type the SEARCH_TOOL_COSTS map and freeze it; consider a safe default in consumers for unknown models.

Untyped constants make it easier to accidentally assign non-numeric values. Typing and freezing improves safety. Also ensure consumer code handles unknown models gracefully (e.g., cost = 0 with a warning).

Apply this diff:

-export const SEARCH_TOOL_COSTS = {
-    'gpt-4': 25 / 1000,
-    'gpt-5': 10 / 1000,
-};
+export const SEARCH_TOOL_COSTS: Readonly<Record<string, number>> = Object.freeze({
+    'gpt-4': 25 / 1000,
+    'gpt-5': 10 / 1000,
+});
packages/core/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts (9)

48-63: Consider typing the legacy 'started' event for consistency

You already route the 'started' legacy event; adding a small interface (like the in_progress/searching/completed types) would keep handlers fully typed and avoid any casts.


149-206: Per-event try/catch is robust; capture non-stop finish reasons if present

Current logic always resolves finishReason to 'stop'. If the SDK emits distinct terminal reasons (e.g., cancelled/incomplete/error), consider extracting a more specific reason from part.response or the event type and forwarding it to the 'interrupted' emitter for better UX/telemetry.


298-300: Minor: avoid double-normalization of model name

getSearchToolCost already normalizes the smythos/ prefix. You can simplify calculateSearchToolUsage accordingly.

Apply this diff to simplify:

-        const modelName = context.modelEntryName?.replace('smythos/', '');
-        const cost = this.getSearchToolCost(modelName);
+        const cost = this.getSearchToolCost(context.modelEntryName);

376-430: Function call start handling is solid; consider initializing arguments explicitly as JSON when known

You initialize arguments as '' until deltas arrive. That’s fine, but if initial arguments are provided on item add (some SDK events include them), prefer persisting the provided string intact to reduce intermediate states.


543-549: Enrich completion event mapping for better 'interrupted' semantics

Currently all unknown/done-like events map to 'stop'. Consider distinguishing common terminal variants.

Apply this diff:

-    private handleCompletionEvent(eventType: string): string {
-        if (eventType === EVENT_TYPES.RESPONSE_COMPLETED || eventType.includes('done')) {
-            return 'stop';
-        }
-        return 'stop'; // Default finish reason
-    }
+    private handleCompletionEvent(eventType: string): string {
+        if (eventType === EVENT_TYPES.RESPONSE_COMPLETED || eventType.endsWith('.completed')) return 'stop';
+        if (eventType.endsWith('.cancelled') || eventType.endsWith('.incomplete')) return 'cancelled';
+        if (eventType.endsWith('.errored') || eventType.endsWith('.error')) return 'error';
+        return 'stop';
+    }

606-629: Clean up unreachable branch when applying tool_choice

validateToolChoice returns true only for known shapes. The “pass-through with type assertion” branch is effectively unreachable and can be removed to reduce ambiguity.

Apply this diff:

-                    } else if (typeof toolChoice === 'object' && toolChoice !== null) {
+                    } else if (typeof toolChoice === 'object' && toolChoice !== null) {
                         // Handle object-based tool choices (specific function selection)
                         if ('type' in toolChoice && toolChoice.type === 'function' && 'function' in toolChoice && 'name' in toolChoice.function) {
                             // Transform Chat Completions specific function choice to Responses API format
                             body.tool_choice = {
                                 type: 'function',
                                 name: toolChoice.function.name,
                             };
-                        } else {
-                            // For other object formats, pass through with type assertion
-                            body.tool_choice = toolChoice as any;
                         }

642-714: transformToolsConfig: propagate strict when provided and use safer defaults for parameters

  • Preserve strict if present on the source (nested in function for Chat Completions or top-level for Responses tools) instead of hardcoding false.
  • Use {} and [] rather than undefined for parameters.properties/required to avoid server-side schema validation issues.

Apply these diffs:

-                    return {
+                    return {
                         type: 'function' as const,
                         name: undefined,
                         description: undefined,
                         parameters: {
                             type: 'object',
-                            properties: undefined,
-                            required: undefined,
+                            properties: {},
+                            required: [],
                         },
-                        strict: false,
+                        strict: false,
                     } as OpenAI.Responses.Tool;
-                    if (!funcTool.name || typeof funcTool.name !== 'string') {
+                    if (!funcTool.name || typeof funcTool.name !== 'string') {
                         return {
                             type: 'function' as const,
                             name: undefined,
                             description: tool.description || '',
-                            parameters: { type: 'object', properties: undefined, required: undefined },
-                            strict: false,
+                            parameters: { type: 'object', properties: {}, required: [] },
+                            strict: Boolean((funcTool as any).strict),
                         } as OpenAI.Responses.Tool;
                     }
-                    return {
+                    return {
                         type: 'function' as const,
                         name: funcTool.name,
                         description: funcTool.description || tool.description || '',
                         parameters: funcTool.parameters || { type: 'object', properties: {}, required: [] },
-                        strict: false,
+                        strict: Boolean((funcTool as any).strict),
                     } as OpenAI.Responses.Tool;
-                if ('parameters' in tool) {
+                if ('parameters' in tool) {
                     return {
                         type: 'function' as const,
                         name: tool.name,
                         description: tool.description || '',
                         parameters: tool.parameters || { type: 'object', properties: {}, required: [] },
-                        strict: false,
+                        strict: typeof (tool as any).strict === 'boolean' ? (tool as any).strict : false,
                     } as OpenAI.Responses.Tool;
                 }

989-1030: Prefer deterministic fallback call_id for testability and reproducibility

Using Date.now() produces non-deterministic artifacts. You can derive a stable fallback from loop indices.

Apply this diff:

-                        transformedMessages.push({
+                        transformedMessages.push({
                             type: 'function_call',
                             name: toolCall.function.name || '',
                             arguments: normalizedArgs,
-                            call_id: toolCall.id || toolCall.call_id || `call_${Date.now()}_${index}`, // Ensure unique ID
+                            call_id: toolCall.id || toolCall.call_id || `call_${i}_${index}`, // Ensure unique and deterministic
                         });

309-312: Consider adding focused unit tests for the new event handlers

applyToolMessageTransformation is covered, but the streaming handlers (output_item.added, function_call_arguments.delta/done, output_item.done, and web search events) would benefit from unit tests to prevent regressions.

I can scaffold vitest tests that simulate these stream events and assert the emitted events and toolsData mutations. Want me to add those?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d1955b0 and 26b7940.

📒 Files selected for processing (8)
  • packages/core/package.json (1 hunks)
  • packages/core/src/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.ts (3 hunks)
  • packages/core/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts (10 hunks)
  • packages/core/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/constants.ts (1 hunks)
  • packages/core/src/subsystems/LLMManager/LLM.service/connectors/openai/types.ts (0 hunks)
  • packages/core/src/types/LLM.types.ts (1 hunks)
  • packages/core/tests/unit/openai/ResponsesApiInterface.unit.test.ts (2 hunks)
  • packages/sdk/package.json (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/core/src/subsystems/LLMManager/LLM.service/connectors/openai/types.ts
🧰 Additional context used
🧬 Code Graph Analysis (2)
packages/core/src/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.ts (4)
packages/core/src/subsystems/MemoryManager/LLMContext.ts (1)
  • messages (28-30)
packages/core/src/helpers/Conversation.helper.ts (1)
  • context (70-72)
packages/core/src/types/LLM.types.ts (1)
  • ILLMRequestContext (450-459)
packages/core/src/subsystems/LLMManager/LLM.helper.ts (1)
  • LLMHelper (8-251)
packages/core/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts (3)
packages/core/src/types/LLM.types.ts (3)
  • ToolData (286-297)
  • TLLMParams (90-164)
  • LLMModelInfo (12-12)
packages/core/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/OpenAIApiInterface.ts (1)
  • ToolConfig (10-15)
packages/core/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/constants.ts (1)
  • SEARCH_TOOL_COSTS (10-13)
🔇 Additional comments (10)
packages/core/package.json (1)

3-3: Version bump looks good.

No concerns on this change.

packages/sdk/package.json (1)

3-3: SDK version bump looks good.

No concerns on this change.

packages/core/tests/unit/openai/ResponsesApiInterface.unit.test.ts (2)

60-61: Test update for strict: false looks correct

Asserting strict: false aligns with the new Responses Tool shape returned by transformToolsConfig.


770-771: Graceful handling of malformed tool config is well-covered

Including strict: false in the error-path expectation keeps the test aligned with the new default tool transformation.

packages/core/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts (6)

313-353: New web search event handlers are clean and side-effect free

These handlers use an immutable upsert and fail safely. Good separation of concerns.


1070-1080: Prefix-based cost resolution is simple and effective

This neatly accommodates variants like gpt-4o/gpt-5*. Looks good.


1086-1118: processFunctionCallResults adds a useful normalization layer

Normalizing arguments and providing a consistent function payload will simplify downstream handling.


1145-1167: Immutable upsert for web search tool is clean

Good use of immutable updates and returning the index for chaining.


226-230: No downstream breakage found — using callId as emitted id looks safe

Quick verification summary:

  • I searched usages of ToolInfo / toolsData and callId across the repo.
  • The OpenAI Responses stream handlers already track both item_id and call_id and preserve callId internally; extractToolCalls maps the final id to callId || id and also keeps callId on the object.
  • Downstream consumers (packages/core/src/helpers/Conversation.helper.ts, packages/core/src/subsystems/LLMManager/LLM.inference.ts, connectors' transformToolMessageBlocks, and the SDK Chat/Agent forwarders) use tool.index, tool.arguments, tool.name, or explicitly honor callId when emitting progress/completion events — none require id to be the original item_id.

Files inspected (representative):

  • packages/core/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts (handles item_id / call_id, extractToolCalls)
  • packages/core/src/helpers/Conversation.helper.ts (consumes toolsData; uses index to drive tool calls)
  • packages/core/src/subsystems/LLMManager/LLM.inference.ts (uses toolsData for context/token accounting and passes to connectors)
  • packages/sdk/src/LLM/Chat.class.ts and packages/sdk/src/Agent/Agent.class.ts (forward ToolInfo)

Conclusion: no code in this repository relies on id being the original item_id-only; keeping id: callId || id and exposing callId is consistent and safe.


31-46: Centralized event strings — verified (OpenAI SDK ^5.12.2, no other raw occurrences found)

package.json shows openai: ^5.12.2, and a repo-wide search for raw response.* event strings returned only the EVENTS definition below; no other hard‑coded occurrences were found.

  • packages/core/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts — defines EVENT_TYPES (the only matches)
  • packages/core/package.json — "openai": "^5.12.2"

No action required; EVENT_TYPES can stay as the single source of truth.

@zubairirfan96 zubairirfan96 merged commit 4f89bf7 into main Aug 15, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants