Repository navigation
fix: Add stream_sources_inline and typed deepnews source batch chunks - #118
Merged
Merged
Conversation
added 4 commits
October 31, 2026 13:08
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by @drafty
🔴 Review tier: HIGH — human review required
mainpublishes a newasknewsrelease to PyPI.stream_sources_inlineonget_deep_news(sync + async), new stream chunk type in both stream unions, two new optionalArticlefields.chore/src-batch-spec), the spec this was written against (0.32.8+ the batch chunk; same contract as emergentmethods/asknews-sdk#7). Merge after !450 is promoted todev.What
Types the deep news inline source-batch streaming feature (engine
v2.0), mirroring chat-servicedto/v1/deepnews.py@4fe4a794/ api !450:CreateDeepNewsRequest.stream_sources_inline: Optional[bool] = False, and the matchingstream_sources_inline: bool = Falsekwarg onChatAPI.get_deep_news/AsyncChatAPI.get_deep_news(all overloads; sent in the request body). Enginev2.0only; off by default.CreateDeepNewsResponseStreamSourceBatch(object: "chat.completion.source.batch";id,created,turn_index,call_index,calls_in_turn,tool_call_id,tool_name,kind: DeepNewsToolCallKind,status: DeepNewsToolCallStatus,source_ids,sources) with thekind-discriminated entry unionCreateDeepNewsResponseStreamSourceBatchEntry=…NewsEntry(SearchResponseDictItem) |…WebEntry(WebSearchResult) |…GraphEntry(AnyUrl) |…ChartEntry(ChartResponse), eachkind+source_id+source+ optionalcitation_number. Sources already streamed are referenced by id only; the finalchat.completion.sourcechunks are unchanged.CreateDeepNewsResponseStreamandCreateDeepNewsResponseStreamV2(tagchat.completion.source.batch). Today,mainraisesValidationError("Input tag 'chat.completion.source.batch' … does not match any of the expected tags") as soon as av2.0stream withstream_sources_inline=trueyields a batch chunk — the untyped gateway already proxies it.Article.centroid_distance/Article.cluster_probability:Optional[float] = None(the spec makes them nullable and no longer required; the SDK model did not carry them at all, so this is additive).No README change (its example is the non-streaming
v1.5call); no version bump, noCHANGELOG.mdedit (commitizen).How I tested
uv sync --frozen, thenuv run coverage run -m pytest -q→ 183 passed (169 onmain+ 14 new intests/api/test_chat_deepnews_source_batch.py), coverage 93 %.ruff check asknews_sdk tests/api/test_chat_deepnews_source_batch.pyclean;ruff format --checkclean on the changed files (ruff 0.16.0 —uvxcould not create its tool dir in the pod, so the pinned binary was used).tests/fixtures/research_canvas/*.jsonl, copied verbatim from api !450 ← chat-service4fe4a794, 44 chunks / 6 batch chunks) through respx as SSE: sync and asyncget_deep_news(stream=True, engine="v2.0", stream_sources_inline=True)yield typedCreateDeepNewsResponseStreamSourceBatchinstances alongside the existing…StreamChunkV2/…StreamSourcechunks; request body carriesstream_sources_inline(and defaults tofalse); recorded serial batches (call-0/call-1,news:2re-found → insource_ids, not re-sent) and mixed-kind batches (web + news sharing a URL →web:1/news:2;search_wikipedia→kind: "wiki"withwebentries) are typed per entry kind; unknown entry kinds are rejected; both unions accept the discriminator;Articlevalidates with the cluster metrics absent,null, or set.main(theValidationErrorquoted above) against the same fixture.Risk / rollback
Additive: existing streams without the flag parse exactly as before; a stream with
stream_sources_inline=truegoes from crashing to yielding typed chunks. Callers iterating av2.0stream should now expect a third chunk type (isinstance(token, CreateDeepNewsResponseStreamSourceBatch)). Merging publishes to PyPI (keep thefix:subject so commitizen cuts a release); revert = revert this one commit, then another release.Docs
Second commit (
docs:), on top ofmain's new cookbook (the branch already containedd8b96e4 chore: Add a cookbook, so no merge/rebase was needed):cookbook/recipes/12_deep_research_basics.py(+ regeneratedcookbook/markdown/12_deep_research_basics.mdviapython cookbook/build.py):stream_sources_inlinerow in the parameter table and a new "Streaming sources per tool call (stream_sources_inline)" section — what it does and why (sources per tool call while research runs, for progressive UIs / citations as they arrive), the requirements (stream=True,engine="v2.0"), a field table forCreateDeepNewsResponseStreamSourceBatch, a runnable sync example that handles the batch chunk, switches on the entrykind(news/web/graph/chart), dedupes bysource_id(re-found sources come back insource_idsonly), resolves each call'ssource_idsagainst what was streamed, and mapscitation_numbers; a short async variant; and the note that the finalchat.completion.sourcemessages are still emitted.cookbook/README.md: recipe 12 row mentions the new section.tests/fixtures/research_canvas/*.jsonl, both files) through the branch's models — 6 batches, 8 unique sources, re-foundnews:2/a2deduped.pytest -q183 passed;ruff check asknews_sdkclean (the cookbook is outsidetask lint, and its existing recipes share the same long-table-line style).Follow-up (not in this PR)
asknews/frontendsendsstream_sources_inlineas an untyped extra key (apps/asknews/src/lib/chat/research-canvas/replay-request.ts:55-62) and parses batch events untyped (v2-adapter.ts:337-340);docs/features/research-canvas/RESEARCH_CANVAS.md:540-542names this as the gap. The TypeScript side is emergentmethods/asknews-sdk#7.CI fix (4740397):
cookbook/is excluded from ruff inpyproject.toml, and thelinttask now resolves its target via a Taskfile var instead of a shellexportthat did not survive betweencmdsentries (which made CI lint the whole repo).task lint→ 0 errors; 183 tests pass.