Skip to content

feat(coze): complete file upload, reasoning stream, and follow-ups#2

Merged
leecyang merged 4 commits into
mainfrom
feat/coze-complete-features
Jul 19, 2026
Merged

feat(coze): complete file upload, reasoning stream, and follow-ups#2
leecyang merged 4 commits into
mainfrom
feat/coze-complete-features

Conversation

@leecyang

@leecyang leecyang commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

Rounds out the Coze integration to match the Coze developer docs' full chat/workflow feature set, building on the existing streaming, tool-call, HITL, and workflow-interrupt support. Two commits:

1. Complete feature set (file upload, reasoning stream, follow-ups):

  • File upload: AsyncCozeClient.upload_file()POST /v1/files/upload (multipart). New file_object() / image_object() / text_object() helpers and object_string message encoding in _message_to_coze, so messages can carry file/image references via HumanMessage(additional_kwargs={"objects": [...]}).
  • Reasoning (thinking) stream: reasoning_content deltas are emitted as separate AIMessageChunks tagged additional_kwargs={"reasoning": True}, and aggregated onto the final AIMessage.
  • Follow-up suggestions: conversation.message.completed events with type=="follow_up" are collected into AIMessage.additional_kwargs["follow_ups"] / response_metadata. CozeAgentNode.suggestions_key is opt-in (defaults to None) so it never breaks strict state schemas.

2. Audit fix + remaining API surface (after re-checking against the Coze docs / reference Python SDK):

  • Bug fix: conversation.message.delta/.completed multiplex answer content with verbose (multi-agent jump info), function_call, tool_output, and knowledge_recall payloads under the same SSE event name. All three streaming call sites only checked the event name, not data.type, so non-answer payloads could leak into the visible answer. Added _is_answer_delta() gating on type in (None, "answer").
  • Added conversation-level endpoints: conversation_retrieve, conversation_message_create (append without triggering a chat run), conversation_message_list (cursor pagination), conversation_message_retrieve.
  • Added file_retrieve (upload status/metadata) and bot_retrieve (/v1/bot/get_online_info).
  • Token usage (token_count/input_count/output_count) from conversation.chat.completed / the polled chat object now populates AIMessage.usage.

All changes are confined to src/lingxigraph/integrations/coze.py and integrations/__init__.py — no changes to core graph/runtime code.

Test plan

  • PYTHONPATH=src python -m pytest tests/test_integrations_v2.py -q — 11 passed (4 existing + 7 new, covering file upload/object_string, reasoning+follow-up streaming, verbose/function_call filtering, token usage, and the new conversation/file/bot endpoints)
  • PYTHONPATH=src python -m pytest tests/ -q — full suite green (136 passed, 2 skipped)
  • Verified suggestions_key=None default doesn't regress the existing test_coze_agent_local_tool_and_workflow_interrupt test (strict TypedDict state schema)

🤖 Generated with Claude Code

leecyang and others added 4 commits July 19, 2026 21:31
Round out the Coze integration to match the Coze developer docs feature
set, on top of existing streaming/tool-call/HITL/workflow-interrupt support:

- File upload: AsyncCozeClient.upload_file() -> POST /v1/files/upload
  (multipart; _request drops the JSON Content-Type so httpx writes the
  boundary). New file_object/image_object/text_object helpers and
  object_string message encoding in _message_to_coze let messages carry
  file/image references via HumanMessage(additional_kwargs={"objects": [...]}).
- Reasoning (thinking) stream: reasoning_content deltas are emitted as
  separate AIMessageChunks tagged additional_kwargs={"reasoning": True} and
  aggregated onto the final AIMessage, so UI adapters can render a thinking
  channel distinctly from the answer.
- Follow-up suggestions: conversation.message.completed with type=="follow_up"
  (and follow_up items from chat/message/list on the polling path) are
  collected into AIMessage.additional_kwargs["follow_ups"] and
  response_metadata. CozeAgentNode.suggestions_key is opt-in (default None) so
  strict state schemas are never broken.

Adds MockTransport contract tests, docs, and a changelog entry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…face

Audit against the Coze developer docs (v3 chat SSE + v1 conversation/file/bot
APIs) surfaced one correctness bug and several missing endpoints on top of
the previous commit:

- Fix: conversation.message.delta/.completed multiplex "answer" content with
  "verbose" (multi-agent jump info), "function_call", "tool_output", and
  "knowledge_recall" payloads under the same event name. All three streaming
  call sites (CozeAgentNode, CozeChatModel.agenerate, CozeChatModel.astream)
  only checked the event name, so non-answer payloads were concatenated into
  the visible answer. Added _is_answer_delta() gating on
  data.type in (None, "answer"); the non-streaming poll path already filtered
  correctly via _split_answer_and_follow_ups.
- Add conversation-level endpoints: conversation_retrieve,
  conversation_message_create (append without triggering a chat run),
  conversation_message_list (cursor pagination: before_id/after_id/limit,
  has_more), conversation_message_retrieve.
- Add file_retrieve (upload status/metadata) and bot_retrieve
  (/v1/bot/get_online_info).
- Extract token usage: conversation.chat.completed's usage
  (token_count/input_count/output_count), and the polled chat object's usage
  field, now populate AIMessage.usage on both streaming and non-streaming
  paths.

Adds MockTransport contract tests for the verbose-filtering fix, the new
endpoints, and usage extraction (136 passed, 2 skipped across the full vendor
suite). Updates docs/integrations-coze.md and CHANGELOG.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rate

The tool-submission continuation branch and the main streaming branch both
bound a local named follow_ups within the same function scope, so mypy
flagged the second binding as redefining the first with an incompatible
inferred type. Renamed the tool-branch variable to tool_follow_ups.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…xtraction

The CI quality gate runs `coverage run --branch -m unittest discover` over
the whole test tree and fails under 80% total branch coverage; the new Coze
endpoints/usage code pushed coze.py's local coverage down enough to trip it
(79% on CI, borderline 80% reproduced locally).

- Add a MockTransport-backed test for CozeAgentNode(stream=False): covers the
  chat()/chat_retrieve() polling loop, usage extraction from the polled chat
  object, and follow-up parsing via chat_messages() - previously entirely
  untested.
- Extend the existing streaming CozeAgentNode test with a
  conversation.chat.completed event carrying usage, covering that branch for
  the streaming path too (previously only CozeChatModel had a usage test).

Local coverage run (coverage run --branch -m unittest discover -s tests,
matching CI's `quality` job) is now 80.42% total, up from the borderline
79-80% before this commit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@leecyang
leecyang merged commit 8697836 into main Jul 19, 2026
6 checks passed
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