Skip to content
This repository was archived by the owner on Feb 1, 2026. It is now read-only.
This repository was archived by the owner on Feb 1, 2026. It is now read-only.

feat(core/providers): add ClaudeProvider implementing StreamingLLMProvider; review OpenAIProvider parity #2

@AlexXLi12

Description

@AlexXLi12

Background

Coevolved defines provider interfaces in coevolved/src/coevolved/core/types.py:

  • LLMProvider.complete(request: LLMRequest) -> LLMResponse
  • StreamingLLMProvider.stream(request: LLMRequest) -> Iterator[LLMStreamChunk]

We already have OpenAIProvider at coevolved/src/coevolved/core/providers/openai.py. We want a Claude/Anthropic provider with comparable behavior, especially around:

  • prompt coercion (PromptPayload.text vs PromptPayload.messages)
  • tool calling (ToolSpec ↔ provider tool format, and parsing tool calls into ToolCall)
  • streaming chunk semantics (LLMStreamChunk, including final finish_reason)

Goals

  • Review OpenAIProvider for correctness/parity with our core.types models (esp. streaming + tool call handling).
  • Implement ClaudeProvider based on StreamingLLMProvider.

Scope / Tasks

1) Review OpenAIProvider

  • Confirm message coercion is correct (PromptPayload → OpenAI messages).
  • Confirm tool spec mapping is correct (ToolSpec.parameters JSON schema → OpenAI tools format).
  • Confirm tool call parsing:
    • non-streaming: tool_calls parsed into ToolCall(arguments: dict)
    • streaming: LLMStreamChunk.tool_call_delta is emitted meaningfully, and final tool calls can be reconstructed by consumers if needed.
  • Identify any gaps (finish_reason, usage, error handling, edge cases).

2) Add ClaudeProvider

  • Create coevolved/src/coevolved/core/providers/claude.py
  • Implement:
    • complete(self, request: LLMRequest) -> LLMResponse
    • stream(self, request: LLMRequest) -> Iterator[LLMStreamChunk]
  • Convert PromptPayload → Claude message format:
    • if messages present: map roles/content
    • if text present: wrap as a single user message
  • Tool calling:
    • map ToolSpec into Claude tool schema format
    • parse Claude tool call requests into ToolCall (id/name/arguments dict)
  • Streaming:
    • yield chunks with text deltas as they arrive
    • if tool deltas are available, populate tool_call_delta similarly to OpenAIProvider
    • ensure the final chunk has finish_reason set (per our protocol docstring)

3) Exports

  • Update coevolved/src/coevolved/core/providers/__init__.py to export ClaudeProvider
  • (Optional) update coevolved/src/coevolved/core/__init__.py to re-export it at coevolved.core.ClaudeProvider

4) Dependency decision

  • Use the official anthropic SDK (likely).
  • Decide whether it is:
    • a required dependency, or
    • an optional extra (preferred if we want minimal installs)

5) Tests

  • Add unit tests for:
    • complete() parses into LLMResponse(text, tool_calls, usage?, finish_reason?)
    • stream() yields LLMStreamChunks and terminates with a final chunk w/ finish_reason
    • tool calling round-trip: ToolSpec → request tool format → tool call parsed back into ToolCall(arguments: dict)

Acceptance criteria

  • ClaudeProvider exists and conforms to StreamingLLMProvider.
  • It can be used by llm_step(...) with LLMRequest(prompt=..., context=LLMConfig(...)).
  • Exports are wired so users can import via coevolved.core.providers (and optionally coevolved.core).
  • Tests cover both streaming and non-streaming behavior, including tool calls.

References (in-repo)

  • Protocols/types: coevolved/src/coevolved/core/types.py
  • Existing provider: coevolved/src/coevolved/core/providers/openai.py

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions