Skip to content

[WIP] feat(messages): add Anthropic Messages API extension#272

Open
Menci wants to merge 5 commits intomoeru-ai:mainfrom
Menci:feat/anthropic-messages
Open

[WIP] feat(messages): add Anthropic Messages API extension#272
Menci wants to merge 5 commits intomoeru-ai:mainfrom
Menci:feat/anthropic-messages

Conversation

@Menci
Copy link
Copy Markdown
Contributor

@Menci Menci commented Mar 14, 2026

No description provided.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request integrates the Anthropic Messages API by introducing a dedicated extension package. This new package provides a structured and efficient way to leverage Anthropic's message-based models, offering robust support for streaming interactions, managing conversational turns, and facilitating tool usage within AI applications.

Highlights

  • New Package: Anthropic Messages API Extension: A new package, @xsai-ext/messages, has been introduced to provide an extension for interacting with the Anthropic Messages API.
  • Comprehensive API Integration: The package includes a full suite of types and utilities for handling Anthropic messages, covering message parameters, content blocks, streaming events, and token usage.
  • Tool Execution Support: Utilities for defining, executing, and managing tools within the Anthropic message flow have been added, enabling advanced conversational capabilities.
  • Streaming Capabilities: The core messages function is designed to handle streaming responses from the Anthropic API, providing event, text, and reasoning text streams, along with step-by-step conversation management.
Changelog
  • docs/content/docs/references/meta.json
    • Updated the documentation metadata to include the new messages extension.
  • packages-ext/messages/README.md
    • Added a README file for the new messages package.
  • packages-ext/messages/package.json
    • Created the package configuration file for @xsai-ext/messages.
  • packages-ext/messages/src/index.ts
    • Exported all public modules from the messages package.
  • packages-ext/messages/src/types/anthropic-message.ts
    • Defined Anthropic message-related type interfaces.
  • packages-ext/messages/src/types/anthropic-tool.ts
    • Defined Anthropic tool-related type interfaces.
  • packages-ext/messages/src/types/finish-reason.ts
    • Defined types for Anthropic stop reasons and generic finish reasons.
  • packages-ext/messages/src/types/messages-options.ts
    • Defined options interfaces for message and token counting functions.
  • packages-ext/messages/src/types/step.ts
    • Defined the interface for a single step in a message conversation.
  • packages-ext/messages/src/types/streaming-event.ts
    • Defined interfaces for various streaming events from the Anthropic API.
  • packages-ext/messages/src/types/usage.ts
    • Defined interfaces for token usage tracking.
  • packages-ext/messages/src/utils/count-tokens.ts
    • Implemented a utility function to count tokens for Anthropic messages.
  • packages-ext/messages/src/utils/execute-tool.ts
    • Implemented a utility function to execute Anthropic tools.
  • packages-ext/messages/src/utils/extract-message-parts.ts
    • Implemented utility functions to extract parts from Anthropic messages.
  • packages-ext/messages/src/utils/map-stop-reason.ts
    • Implemented a utility to map Anthropic stop reasons.
  • packages-ext/messages/src/utils/messages.ts
    • Implemented the core messages function for Anthropic API interaction.
  • packages-ext/messages/src/utils/request-headers.ts
    • Implemented a utility to construct request headers for Anthropic API.
  • packages-ext/messages/src/utils/streaming-event-parser-stream.ts
    • Implemented a stream parser for Anthropic streaming events.
  • packages-ext/messages/src/utils/tool.ts
    • Implemented a helper function to create executable tools.
  • packages-ext/messages/src/utils/wrap-tool-result.ts
    • Implemented a utility to wrap tool execution results.
  • packages-ext/messages/test/count-tokens.test.ts
    • Added unit tests for the countTokens utility.
  • packages-ext/messages/test/index.test.ts
    • Added unit tests for the main messages function and tool interactions.
  • packages-ext/messages/test/tool.test.ts
    • Added unit tests for the tool helper function.
  • packages-ext/messages/test/utils.ts
    • Added utility functions for creating mock responses in tests.
  • pnpm-lock.yaml
    • Updated the dependency lock file.
Activity
  • The pull request was created by Menci with the title "[WIP] feat(messages): add Anthropic Messages API extension".
  • The pull request is marked as "[WIP]", indicating it is still under development.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

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

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 a new package, @xsai-ext/messages, to provide an extension for the Anthropic Messages API. This is a significant feature addition that includes the core streaming logic, type definitions, utility functions, and tests. My review focuses on improving code correctness and maintainability. I've identified a few areas for improvement, including moving a dev dependency to dependencies, simplifying some complex type definitions, refactoring for better state management, and adding error handling for JSON parsing in a stream. Overall, this is a solid foundation for the new package.

Comment on lines +8 to +11
transform: async (chunk, controller) => {
const event = JSON.parse(chunk.data) as StreamingEvent
controller.enqueue(event)
},
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.

high

The JSON.parse call is not wrapped in a try-catch block. If the chunk.data from the event stream is not valid JSON, this will throw an unhandled exception and crash the stream. It's safer to wrap this in a try-catch block and call controller.error() on failure.

      transform: async (chunk, controller) => {
        try {
          const event = JSON.parse(chunk.data) as StreamingEvent
          controller.enqueue(event)
        } catch (error) {
          controller.error(error)
        }
      },

Comment on lines +44 to +47
"devDependencies": {
"@standard-schema/spec": "catalog:",
"zod": "catalog:schema-dev"
}
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.

medium

The @standard-schema/spec package is used for types in the public API of this package (e.g., in src/types/anthropic-tool.ts). Therefore, it should be listed under dependencies instead of devDependencies to ensure its types are available to consumers of this package.

import type { Usage } from './usage'

export interface AnthropicAssistantMessageParam {
content: (AnthropicRedactedThinkingBlock | AnthropicTextBlockParam | AnthropicThinkingBlockParam | AnthropicToolUseBlock)[] | AnthropicRedactedThinkingBlock[] | AnthropicTextBlockParam[] | AnthropicThinkingBlockParam[] | AnthropicToolUseBlock[] | string
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.

medium

The union type for content is unnecessarily verbose. A type like (A | B)[] | A[] | B[] can be simplified to (A | B)[]. Applying this simplification will make the type definition cleaner and easier to read.

Suggested change
content: (AnthropicRedactedThinkingBlock | AnthropicTextBlockParam | AnthropicThinkingBlockParam | AnthropicToolUseBlock)[] | AnthropicRedactedThinkingBlock[] | AnthropicTextBlockParam[] | AnthropicThinkingBlockParam[] | AnthropicToolUseBlock[] | string
content: (AnthropicRedactedThinkingBlock | AnthropicTextBlockParam | AnthropicThinkingBlockParam | AnthropicToolUseBlock)[] | string

}

export interface AnthropicUserMessageParam {
content: (AnthropicDocumentBlockParam | AnthropicImageBlockParam | AnthropicTextBlockParam | AnthropicToolResultBlockParam)[] | AnthropicDocumentBlockParam[] | AnthropicImageBlockParam[] | AnthropicTextBlockParam[] | AnthropicToolResultBlockParam[] | string
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.

medium

The union type for content is unnecessarily verbose. A type like (A | B)[] | A[] | B[] can be simplified to (A | B)[]. Applying this simplification will make the type definition cleaner and easier to read.

Suggested change
content: (AnthropicDocumentBlockParam | AnthropicImageBlockParam | AnthropicTextBlockParam | AnthropicToolResultBlockParam)[] | AnthropicDocumentBlockParam[] | AnthropicImageBlockParam[] | AnthropicTextBlockParam[] | AnthropicToolResultBlockParam[] | string
content: (AnthropicDocumentBlockParam | AnthropicImageBlockParam | AnthropicTextBlockParam | AnthropicToolResultBlockParam)[] | string

Comment on lines +211 to +244
const handleMessageStop = async ({ conversation, currentStep, steps, tools, totalUsage }: HandleMessageStopOptions): Promise<ProcessEventResult> => {
const message = finalizeCurrentMessage(currentStep)
const stepUsage = message.usage
const nextUsage = stepUsage
const nextTotalUsage = addUsage(totalUsage, stepUsage)
const step = createStep(message, [])

conversation.push(createAssistantMessageParam(message))
steps.push(step)

const shouldContinue = message.stop_reason === 'tool_use' && step.toolUses.length > 0

if (shouldContinue) {
const results = await Promise.all(
step.toolUses.map(async toolUse => executeTool({ tools, toolUse })),
)

for (const { toolResult } of results) {
step.toolResults.push(toolResult)
}

conversation.push({
content: step.toolResults,
role: 'user',
})
}

return {
currentStep: undefined,
shouldContinue,
totalUsage: nextTotalUsage,
usage: nextUsage,
}
}
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.

medium

The step object is mutated by pushing to step.toolResults after it has already been pushed to the steps array. This can make the code harder to reason about. It would be cleaner to gather all information for the step before creating it, and then push the immutable step object to the array.

const handleMessageStop = async ({ conversation, currentStep, steps, tools, totalUsage }: HandleMessageStopOptions): Promise<ProcessEventResult> => {
  const message = finalizeCurrentMessage(currentStep)
  const stepUsage = message.usage
  const nextUsage = stepUsage
  const nextTotalUsage = addUsage(totalUsage, stepUsage)

  conversation.push(createAssistantMessageParam(message))

  const toolUses = getToolUses(message)
  const shouldContinue = message.stop_reason === 'tool_use' && toolUses.length > 0

  let toolResults: AnthropicToolResultBlockParam[] = []
  if (shouldContinue) {
    const results = await Promise.all(
      toolUses.map(async toolUse => executeTool({ tools, toolUse })),
    )
    toolResults = results.map(({ toolResult }) => toolResult)

    conversation.push({
      content: toolResults,
      role: 'user',
    })
  }

  const step = createStep(message, toolResults)
  steps.push(step)

  return {
    currentStep: undefined,
    shouldContinue,
    totalUsage: nextTotalUsage,
    usage: nextUsage,
  }
}

@kwaa
Copy link
Copy Markdown
Member

kwaa commented Mar 15, 2026

Unfortunately, I currently do not plan to support the Anthropic format. I support the Chat Completion API due to its widespread usage, and the Responses API because it is based on an open standard. (https://openresponses.org)

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