Skip to content

Close the gaps where the 0.6 features met each other - #23

Merged
ardada2468 merged 1 commit into
mainfrom
chore/post-merge-followups
Aug 22, 2026
Merged

ardada2468 merged 1 commit into
mainfrom
chore/post-merge-followups

Conversation

@ardada2468

Copy link
Copy Markdown
Owner

Follow-ups from merging the fifteen 0.6 feature branches. Each item is something a single unit could not fix on its own, because it needed another unit to have landed first.

Images reach Predict

Vision landed complete at the ChatMessage and provider layers, but Predict built a string prompt, so a signature declaring an image input had it flattened to [image: image/png] before the request was assembled — the model never saw the picture. Predict and ChainOfThought now build content parts whenever an image field is supplied, and a plain string otherwise, so text-only programs send byte-for-byte what they always did.

Structured output over an image asks for the schema in the prompt, because the provider methods that constrain decoding accept only a string. Validation is unchanged; only the guarantee that the model cannot emit the wrong shape is lost. That is documented in the docs and in a callout.

Provider cache scoping

The caching unit added a cacheScope() hook but could not touch the provider files. Until now AnthropicLM({ maxTokens: 64 }) and AnthropicLM({ maxTokens: 8192 }) hashed identically, as did Gemini clients differing in safetySettings and OpenAI clients differing in baseURL — so one could be served a reply the other's configuration would never have produced. All four providers now override it, with a regression test.

AnthropicRefusalError

Was an alias of ContentFilterError, which meant instanceof AnthropicRefusalError also matched OpenAI and Gemini content filters. It is now a subclass: a cross-provider catch on ContentFilterError still works, and narrowing to Anthropic means Anthropic again.

Housekeeping

  • evaluate() delegates to the shared pool in utils/pool instead of carrying a second copy. Its 0 → 1 lane clamp stays, since the shared pool rejects a non-positive limit outright.
  • site/examples.html used a StubLM snippet calling super({ model: 'stub' }), which never matched BaseLM's (provider, model) constructor. It now uses the MockLM that ships at @ts-dspy/core/testing.
  • .claude/ is excluded from eslint and prettier. Git worktrees live there, so npm run lint was failing on other branches' checkouts.

Verification

build, lint, typecheck, format:check, verify:packaging all clean; 718 tests pass. Docs verified in a browser: 21 sections numbered 01–21, TOC order matching exactly, all 61 code blocks highlighted, no horizontal overflow.

🤖 Generated with Claude Code

Images now reach the model through Predict and ChainOfThought. A signature
declaring an image input had it flattened to an [image: ...] placeholder
before the request was built, so the model never saw the picture.

Every provider overrides cacheScope(). Two clients differing only in
maxTokens, safetySettings, baseURL or declared capabilities hashed to the
same key, so one could be served a reply the other would never produce.

AnthropicRefusalError becomes a subclass of ContentFilterError rather than
an alias, so narrowing to Anthropic means Anthropic again while a
cross-provider catch still works.

Also: evaluate() delegates to the shared concurrency pool instead of
carrying its own, the examples page uses the published MockLM rather than
a StubLM snippet that never compiled, and .claude/ is excluded from lint
and format so a local git worktree cannot break either.
@ardada2468
ardada2468 merged commit 43b0d84 into main Aug 22, 2026
7 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