Skip to content

feat: Introduce ManagedAgent and AgentRunner implementations#110

Open
jsonbailey wants to merge 6 commits intomainfrom
jb/aic-1664/managed-agent
Open

feat: Introduce ManagedAgent and AgentRunner implementations#110
jsonbailey wants to merge 6 commits intomainfrom
jb/aic-1664/managed-agent

Conversation

@jsonbailey
Copy link
Copy Markdown
Contributor

@jsonbailey jsonbailey commented Mar 25, 2026

feat: Add OpenAIAgentRunner with agentic tool-calling loop
feat: Add LangChainAgentRunner with agentic tool-calling loop
feat: Add OpenAIRunnerFactory.create_agent(config, tools) -> OpenAIAgentRunner
feat: Add LangChainRunnerFactory.create_agent(config, tools) -> LangChainAgentRunner
feat: Add ManagedAgent wrapper holding AgentRunner and LDAIConfigTracker
feat: Add LDAIClient.create_agent() returning ManagedAgent

Requirements

  • I have added test coverage for new or changed functionality
  • I have followed the repository's pull request submission guidelines
  • I have validated my changes against all supported platform versions

Related issues

Provide links to any issues in this repository or elsewhere relating to this pull request.

Describe the solution you've provided

Provide a clear and concise description of what you expect to happen.

Describe alternatives you've considered

Provide a clear and concise description of any alternative solutions or features you've considered.

Additional context

Add any other context about the pull request here.


Note

Medium Risk
Introduces new agent execution path (including OpenAI tool-calling loop) and a new LDAIClient.create_agent API, which could affect runtime behavior and metrics/usage tracking for agent configs. Risk is moderated by added unit tests, but tool execution/looping adds complexity and potential edge cases.

Overview
Adds first-class managed agent invocation to the server AI SDK via ManagedAgent and LDAIClient.create_agent(), including event tracking for agent creation.

Implements provider-side agent runners for OpenAI (OpenAIAgentRunner) and LangChain (LangChainAgentRunner) plus create_agent() factory methods; OpenAI runs an explicit tool-calling loop and aggregates token usage across turns, while LangChain delegates to a compiled agent graph.

Extends tool support and usage handling: LangChain can now bind/construct tools from config + ToolRegistry, and both OpenAI/LangChain usage extractors now return None when all token counts are zero. Test suites are expanded to cover agent creation/execution, tool-call parsing, and usage edge cases.

Written by Cursor Bugbot for commit e4b3830. This will update automatically on new commits. Configure here.

@jsonbailey jsonbailey changed the title feat: Introduce ManagedAgent and AgentRunner implementations feat: Introduce ManagedAgent and AgentRunner implementations (PR-5) Mar 25, 2026
@jsonbailey jsonbailey force-pushed the jb/aic-1664/managed-agent branch from c3f2da2 to bc8b945 Compare March 26, 2026 13:27
@jsonbailey jsonbailey changed the base branch from jb/aic-1664/runner-abcs to jb/aic-1664/graph-tracking-improvements March 26, 2026 13:28
@jsonbailey jsonbailey marked this pull request as ready for review March 26, 2026 13:28
@jsonbailey jsonbailey requested a review from a team as a code owner March 26, 2026 13:28
Copy link
Copy Markdown
Member

@keelerm84 keelerm84 left a comment

Choose a reason for hiding this comment

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

Looks like bugbot has some good feedback on this one.

@jsonbailey jsonbailey force-pushed the jb/aic-1664/graph-tracking-improvements branch from 886e3b7 to a183f12 Compare March 26, 2026 17:49
Base automatically changed from jb/aic-1664/graph-tracking-improvements to main March 26, 2026 18:51
feat: Add OpenAIAgentRunner with agentic tool-calling loop
feat: Add LangChainAgentRunner with agentic tool-calling loop
feat: Add OpenAIRunnerFactory.create_agent(config, tools) -> OpenAIAgentRunner
feat: Add LangChainRunnerFactory.create_agent(config, tools) -> LangChainAgentRunner
feat: Add ManagedAgent wrapper holding AgentRunner and LDAIConfigTracker
feat: Add LDAIClient.create_agent() returning ManagedAgent
…ider helper tests

feat: add TestGetAIUsageFromResponse and TestGetToolCallsFromResponse test coverage for LangChainHelper
feat: add TestGetAIUsageFromResponse test coverage for OpenAIHelper
fix: update ManagedAgent.invoke to use track_metrics_of_async
@jsonbailey jsonbailey force-pushed the jb/aic-1664/managed-agent branch from bc8b945 to c1b87a6 Compare March 26, 2026 19:20
@jsonbailey jsonbailey changed the title feat: Introduce ManagedAgent and AgentRunner implementations (PR-5) feat: Introduce ManagedAgent and AgentRunner implementations Mar 26, 2026
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
continue
if "type" in td:
# Already in OpenAI format
tools.append(td)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

OpenAI runner passes unsupported tool types to API

Medium Severity

_build_openai_tools blindly passes through any tool definition that has a type key, including non-function built-in types like code_interpreter. The Chat Completions API only supports type: 'function', so a single unsupported tool type in the config causes the entire agent run to fail with an API error. The LangChain resolver _resolve_tools_for_langchain correctly filters and warns about non-function types, but the OpenAI runner lacks equivalent logic.

Additional Locations (1)
Fix in Cursor Fix in Web

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 3 potential issues.

There are 4 total unresolved issues (including 1 from previous review).

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

continue
if "type" in td:
# Already in OpenAI format
tools.append(td)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Tool format check mishandles LD definitions with type field

High Severity

_build_openai_tools checks if "type" in td and assumes the definition is already in OpenAI format, passing it through unchanged. However, LD tool definitions can include a type field (e.g., {'type': 'function', 'name': 'get-weather', ...}), which the LangChain helper explicitly handles. When such a definition is encountered, it gets passed to OpenAI in the flat LD format instead of the required nested format ({'type': 'function', 'function': {'name': ..., ...}}), causing the API call to fail.

Fix in Cursor Fix in Web

"role": "tool",
"tool_call_id": tool_call.id,
"content": result,
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Agent loop lacks maximum iteration guard

Medium Severity

The while True agentic loop in OpenAIAgentRunner.run has no maximum iteration limit. If the model keeps returning tool calls (e.g., due to a misconfigured tool that always triggers re-invocation, or a model that loops on unavailable tools), this runs indefinitely — burning API tokens and potentially hanging the process. The LangChain equivalent delegates to lc_create_agent, which has a built-in recursion limit. A similar safeguard is missing here.

Fix in Cursor Fix in Web

description=td.get('description', ''),
))

return structured
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Duplicated tool-filtering logic in two functions

Low Severity

build_structured_tools and _resolve_tools_for_langchain contain nearly identical tool-filtering logic: checking isinstance(td, dict), checking the type field, extracting name, verifying registry membership, and logging the same warnings. The only difference is the output format (dicts vs StructuredTool). Extracting the shared filtering into a common helper would reduce duplication and the risk of inconsistent updates.

Additional Locations (1)
Fix in Cursor Fix in Web

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