Skip to content

Comments

fix: correct session.chat() return type to match server response#45

Open
illera88 wants to merge 1 commit intoanomalyco:mainfrom
illera88:fix/session-chat-response-type
Open

fix: correct session.chat() return type to match server response#45
illera88 wants to merge 1 commit intoanomalyco:mainfrom
illera88:fix/session-chat-response-type

Conversation

@illera88
Copy link

Summary

  • Adds SessionChatResponse type with info: Message and parts: List[Part] fields matching the actual server response.
  • Updates both sync and async chat() methods to return SessionChatResponse instead of bare AssistantMessage.

Problem

The OpenCode server's POST /session/{id}/message endpoint returns a wrapper:

{
  "info": { "role": "assistant", "id": "msg_...", "tokens": {...}, ... },
  "parts": [
    { "type": "step-start", ... },
    { "type": "text", "text": "Hello!", ... },
    { "type": "step-finish", ... }
  ]
}

But session.chat() was typed to cast this entire response to AssistantMessage, which only expects the fields inside info. Pydantic silently ignores the unknown top-level keys (info, parts) and produces a hollow object where id, role, tokens, etc. are all None.

Reproduction

from opencode_ai import Opencode

client = Opencode(base_url="http://127.0.0.1:4096")
session = client.session.create(extra_body={})

resp = client.session.chat(
    session.id,
    model_id="claude-sonnet-4-20250514",
    provider_id="anthropic",
    parts=[{"type": "text", "text": "Hello"}],
)

print(resp.id)    # None (should be "msg_...")
print(resp.role)  # None (should be "assistant")

Fix

Added SessionChatResponse (same shape as SessionMessagesResponseItem):

class SessionChatResponse(BaseModel):
    info: Message
    parts: List[Part]

After this fix:

resp = client.session.chat(session.id, ...)
print(resp.info.role)     # "assistant"
print(resp.info.id)       # "msg_..."
print(resp.info.tokens)   # Tokens(input=3, output=9, ...)
for part in resp.parts:
    if isinstance(part, TextPart):
        print(part.text)  # "Hello!"

The OpenCode server's POST /session/{id}/message endpoint returns a
wrapper object with 'info' (the assistant message) and 'parts' (the
message parts):

    {"info": AssistantMessage, "parts": Part[]}

The SDK was casting this response to bare AssistantMessage, which
caused Pydantic to silently produce a hollow object where all fields
(id, role, tokens, etc.) were None.

This commit:
- Adds SessionChatResponse type with 'info: Message' and 'parts: List[Part]'
  fields matching the actual server response shape
- Updates both sync and async chat() to return SessionChatResponse
- Exports the new type from types/__init__.py
- Updates api.md documentation
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.

1 participant