Skip to content

Feat/1479 OpenAI server#1507

Open
Veselin-Vasilev-IBM-1 wants to merge 10 commits into
i-am-bee:mainfrom
Veselin-Vasilev-IBM-1:feat/1479-openai-server
Open

Feat/1479 OpenAI server#1507
Veselin-Vasilev-IBM-1 wants to merge 10 commits into
i-am-bee:mainfrom
Veselin-Vasilev-IBM-1:feat/1479-openai-server

Conversation

@Veselin-Vasilev-IBM-1

Copy link
Copy Markdown

Which issue(s) does this pull-request address?

Closes: #1479

Description

Adds an OpenAI-compatible server adapter for TypeScript, porting the functionality from the existing Python implementation (python/beeai_framework/adapters/openai/serve).

This exposes any BeeAI agent as a standard OpenAI /v1/chat/completions and /v1/responses endpoint, enabling integration with tools like Watsonx Orchestrate that consume these APIs (ref: discussion #1008).

What's included:

  • OpenAIServer class following the same pattern as the existing A2AServer and MCPServer adapters, using BaseAgent as the interim abstraction. Configurable via api: "chat-completion" | "responses".
  • ChatCompletionAPI: Express-based router handling /v1/chat/completions with both non-streaming and SSE streaming responses.
  • ResponsesAPI: Express-based router handling the newer /v1/responses endpoint, matching the Python implementation's object formats and streaming event sequences (response.created, response.in_progress, etc.).
  • Message conversion utilities (openaiMessageToBeeAIMessage and openaiInputToBeeAIMessage) to translate between OpenAI and BeeAI message formats.
  • Example usage at examples/serve/openai.ts and examples/serve/openai_responses.ts.
  • Unit tests (11/11 passing).

No breaking changes.

Checklist

General

  • I have read the appropriate contributor guide
  • I have signed off on my commit
  • Commit messages and PR title follow conventional commits
  • Appropriate label(s) added to PR: TypeScript

Code quality checks

  • Code quality checks pass: mise checkyarn tsc --noEmit passes cleanly (0 errors from this PR; 2 pre-existing errors in watsonx/backend/chat.ts). Full mise check could not run locally due to missing pipx/GitHub API auth in tool setup.

Testing

  • Unit tests pass
  • E2E tests pass — no E2E tests needed; consistent with existing serve adapters (A2AServer, MCPServer) which have no E2E tests. However, both endpoints have been manually verified end-to-end via curl against a local Ollama instance (granite4:micro), confirming both string/array inputs and proper JSON output formats.
  • Tests are included (for bug fixes or new features)

Documentation

  • Documentation is updated
  • Embedme embeds code examples in docs

@Veselin-Vasilev-IBM-1 Veselin-Vasilev-IBM-1 requested a review from a team as a code owner June 17, 2026 14:44
@github-actions github-actions Bot added the typescript Typescript related functionality label Jun 17, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces an OpenAI-compatible server adapter for the BeeAI framework, enabling agents to be served via standard Chat Completions and Responses APIs with streaming support. Feedback focuses on improving server robustness and lifecycle management: wrapping the Express server startup in a Promise to handle errors, safely parsing authorization headers to prevent runtime crashes, cleaning up streaming event listeners on client disconnect to avoid memory leaks, and wrapping JSON parsing of tool arguments in try-catch blocks.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread typescript/src/adapters/openai/serve/server.ts Outdated
Comment thread typescript/src/adapters/openai/serve/api.ts Outdated
Comment thread typescript/src/adapters/openai/serve/responses_api.ts Outdated
Comment thread typescript/src/adapters/openai/serve/api.ts Outdated
Comment thread typescript/src/adapters/openai/serve/responses_api.ts Outdated
Comment thread typescript/src/adapters/openai/serve/utils.ts
@dosubot dosubot Bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Jun 17, 2026
@Veselin-Vasilev-IBM-1 Veselin-Vasilev-IBM-1 marked this pull request as draft July 2, 2026 14:33

@Tomas2D Tomas2D left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for porting the OpenAI server to TypeScript, @Veselin-Vasilev-IBM-1 — the overall structure (extending Server<…>, per-request agent/memory cloning, abort handling) follows our A2AServer/MCPServer pattern well. A few things need addressing before this can merge:

🔴 Security — reintroduces a vulnerability we just patched (blocking). OpenAIServerConfig.host defaults to "0.0.0.0" with apiKey undefined, so serve() binds an unauthenticated server to all interfaces by default — and examples/serve/openai_responses.ts even hard-codes host: "0.0.0.0". This is exactly the issue (#1516) that was just fixed on the Python side in beb08b3a (default to loopback 127.0.0.1, warn on non-loopback bind without an API key). Please mirror that fix here and drop the 0.0.0.0 from the example.

🔴 Correctness — streaming emits no content for the example agent (blocking). Both streaming handlers filter for event.name === "update", but ToolCallingAgent (used in both examples) only emits start/success — only ReActAgent emits update. So stream: true against the documented example yields an SSE envelope with zero text deltas. Please emit from the events the agent actually produces (or switch the example agent).

Other:

  • The branch currently conflicts with main (DIRTY) — please rebase/resolve.
  • The fork-gated Lint & Build & Test workflow hasn't run yet; it needs to pass.
  • Minor: sequence_number in the Responses stream is stale/duplicated (built before sendEvent increments), violating the monotonic-sequence contract; and the docs Examples section isn't updated.

Happy to help with the loopback-default port if useful.

Signed-off-by: Veselin-Vasilev-IBM-1 <veselin.vasilev1@ibm.com>
Signed-off-by: Veselin-Vasilev-IBM-1 <veselin.vasilev1@ibm.com>
Signed-off-by: Veselin-Vasilev-IBM-1 <veselin.vasilev1@ibm.com>
Signed-off-by: Veselin-Vasilev-IBM-1 <veselin.vasilev1@ibm.com>
Signed-off-by: Veselin-Vasilev-IBM-1 <veselin.vasilev1@ibm.com>
Signed-off-by: Veselin-Vasilev-IBM-1 <veselin.vasilev1@ibm.com>
- Use .observe() + emitter.match() on the run instead of clonedAgent.emitter.on()
 to correctly subscribe to agent run events using the BeeAI pattern
- Add try/catch around the streaming run() in ChatCompletionAPI so agent
 errors after SSE headers are sent produce a proper error chunk instead of
 an unhandled rejection
- Pass SystemMessage through transformRequestMessages instead of silently
 dropping it, so callers' system prompts are respected
- Remove cast to ReActAgentRunOutput in non-streaming paths; use the generic
 result.result.text which works for all agent types
- Fix ResponsesStreamPartOutputText.type literal to "output_text" (was
 incorrectly "response.output_text.done" per OpenAI spec)
- Remove double-mount at "/" in OpenAIServer.serve(); keep only "/v1" to
 match the OpenAI URL structure
- Fix indentation inconsistency in utils.ts assistant tool-call loop

Signed-off-by: Veselin-Vasilev-IBM-1 <veselin.vasilev1@ibm.com>
…rver adapter

- Improve Authorization header check using regex
- Wrap tool call arguments JSON parsing in try-catch to avoid unhandled exceptions
- Add checks for `res.writableEnded` and `res.destroyed` to avoid unhandled stream write rejections
- Fix eslint curly brace and unused variable warnings
- Run prettier for formatting consistency

Signed-off-by: Veselin-Vasilev-IBM-1 <veselin.vasilev1@ibm.com>
…nce fixes to TS

Mirrors the Python-side security/correctness fixes (beb08b3) in the TypeScript OpenAI server adapter and addresses remaining code-review comments.

Security (i-am-bee#1516):
- Default OpenAIServerConfig.host to 127.0.0.1 (loopback); binding all
  interfaces (0.0.0.0) is now opt-in.
- Warn when serving on a non-loopback host with no apiKey set.
- Drop the hard-coded host: '0.0.0.0' from examples/serve/openai_responses.ts.

Correctness (streaming):
- ChatCompletionAPI and ResponsesAPI now emit text deltas from the events
  the agent actually produces. Previously both handlers filtered for
  event.name === 'update', but ToolCallingAgent only emits start/success,
  so stream: true against the documented example yielded an SSE envelope
  with zero text deltas. Now 'success' (state.result.text) is also emitted,
  keeping streaming working for both ReActAgent and ToolCallingAgent.

Sequence number:
- sendEvent() now increments sequence_number before assigning it to the
  event payload, instead of pre-building events with a stale/duplicated
  sequence_number, violating the monotonic-sequence contract.
- Removed redundant sequence_number fields from event objects; the type is
  now optional and populated centrally by sendEvent.

Docs:
- Replace the 'coming soon' TypeScript placeholder in
  integrations/openai-api.mdx with the real openai_responses.ts example.

Tests:
- Add unit tests mirroring Python's test_openai_server_config.py: default
  host is loopback; warning fires for non-loopback without apiKey; no
  warning on loopback or when apiKey is set. Expose the module-level logger
  so tests can spy on warn().

Signed-off-by: Veselin-Vasilev-IBM-1 <veselin.vasilev1@ibm.com>
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Jul 3, 2026
Replace 0.0.0.0 with 127.0.0.1 in the curl examples for the Responses
and Chat Completion APIs, consistent with the security fix that defaults
OpenAIServer to loopback (127.0.0.1) and warns on non-loopback binds
without an API key.

Signed-off-by: Veselin-Vasilev-IBM-1 <veselin.vasilev1@ibm.com>
@Veselin-Vasilev-IBM-1

Copy link
Copy Markdown
Author

Thanks @Tomas2D ,
All blocking issues from the review have been addressed:

🔴 Security (loopback default)host now defaults to 127.0.0.1 in OpenAIServerConfig. Binding to a non-loopback host without apiKey emits a logger.warn. The 0.0.0.0 hard-code has been removed from the examples.
Mirrors the Python fix in beb08b3a. Unit tests added covering: default host is 127.0.0.1, warns on non-loopback without apiKey, no warning on loopback without apiKey, no warning on non-loopback with apiKey set.

🔴 Streaming correctness — Both ChatCompletionAPI and ResponsesAPI now use emitter.match("*.*", ...) and handle both event.name === "update" (ReActAgent incremental tokens) and event.name === "success" (ToolCallingAgent final result). Streaming now produces content for both agent types.

Other fixes:

  • sequence_number in the Responses stream is now incremented before assignment in sendEvent, ensuring a strictly monotonic sequence.
  • Branch rebased cleanly on main, no conflicts.

Local validation:

  • tsc --noEmit — 0 errors from this PR (2 pre-existing in watsonx/backend/chat.ts, unrelated)
  • All 15 unit tests pass (vitest run tests/adapters/openai)
  • E2E verified manually against Ollama (granite4:micro) — all 4 scenarios pass: Chat Completions non-streaming, Chat Completions streaming, Responses API non-streaming, Responses API streaming

Can you elaborate more on the "Lint & Build & Test" workflow - is this something I need to trigger ?

@Veselin-Vasilev-IBM-1 Veselin-Vasilev-IBM-1 marked this pull request as ready for review July 3, 2026 14:36
@Veselin-Vasilev-IBM-1 Veselin-Vasilev-IBM-1 requested a review from a team as a code owner July 3, 2026 14:36
@dosubot dosubot Bot added size:XXL This PR changes 1000+ lines, ignoring generated files. and removed size:XL This PR changes 500-999 lines, ignoring generated files. labels Jul 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size:XXL This PR changes 1000+ lines, ignoring generated files. typescript Typescript related functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add OpenAI-compatible server (OpenAIServer) to TypeScript

2 participants