Provider & embeddings intra-file duplication
Not cross-provider (wire protocols differ — the one shareable family already lives in _openai_compat.py). The duplication is within each file, between sync and async paths.
Gemini (src/selectools/gemini_provider.py): the HttpOptions + GenerateContentConfig + tools block is repeated ×4 (177-188, 300-311, 573-584, 692-703); the function_call → ToolCall conversion (with base64 thought_signature) ×4 (217, 365, 613, 752). A comment at :633 even says "see complete() for context." Densest in the providers.
Anthropic (src/selectools/anthropic_provider.py): request_args dict ×4 (189-200, 275-286, 565-576, 651-662); content-block parse loop ×2; usage-extraction ×2; streaming event state-machine ×2 (sync/async, identical but for for/async for).
Embeddings (M6): _get_model_dimension (substring-match + warning fallback) ×4 (openai/anthropic/cohere/gemini); the trivial dimension property re-declared ×4 only for a docstring → pull into the EmbeddingProvider ABC; anthropic+cohere share an identical except TypeError: older-client fallback.
Fix
Per-file private helpers (_build_config/_toolcall_from_part for Gemini; _build_request_args/_usage_from_response/_message_from_content_blocks for Anthropic). Pull the embeddings dimension property into the ABC. Do NOT build a cross-provider build_usage — the three cache-token field shapes make it ugly.
Acceptance
- Sync/async paths in each provider share the extracted helper; provider tests pass.
Provider & embeddings intra-file duplication
Not cross-provider (wire protocols differ — the one shareable family already lives in
_openai_compat.py). The duplication is within each file, between sync and async paths.Gemini (
src/selectools/gemini_provider.py): theHttpOptions+GenerateContentConfig+ tools block is repeated ×4 (177-188, 300-311, 573-584, 692-703); thefunction_call→ToolCallconversion (with base64thought_signature) ×4 (217, 365, 613, 752). A comment at :633 even says "see complete() for context." Densest in the providers.Anthropic (
src/selectools/anthropic_provider.py):request_argsdict ×4 (189-200, 275-286, 565-576, 651-662); content-block parse loop ×2; usage-extraction ×2; streaming event state-machine ×2 (sync/async, identical but forfor/async for).Embeddings (M6):
_get_model_dimension(substring-match + warning fallback) ×4 (openai/anthropic/cohere/gemini); the trivialdimensionproperty re-declared ×4 only for a docstring → pull into theEmbeddingProviderABC; anthropic+cohere share an identicalexcept TypeError:older-client fallback.Fix
Per-file private helpers (
_build_config/_toolcall_from_partfor Gemini;_build_request_args/_usage_from_response/_message_from_content_blocksfor Anthropic). Pull the embeddingsdimensionproperty into the ABC. Do NOT build a cross-providerbuild_usage— the three cache-token field shapes make it ugly.Acceptance