+note = "Retrieval-provider Cohere /v2/embed wire mapping (§8.4) -- the embed half of §8.4, extending the vendor section to both Cohere endpoints (the §8.2 Jina embed+rerank pattern). CohereEmbeddingProvider posts POST {base_url}/v2/embed with {model, input_type, texts (string array), embedding_types: [float], truncate: NONE, output_dimension?}; base_url defaults to https://api.cohere.com (origin only; the provider appends /v2/embed), sent with api_key as Authorization: Bearer. input_type is a MANDATORY wire field (Cohere v2 requires it), so the mapping always sends a value from the closed set: query -> search_query, document -> search_document, and an ABSENT OA input_type -> search_document (the bulk-indexing default -- the case no other embed mapping has, since §8.1 / §8.2 omit the field when absent and §8.3 has none); an unrecognized OA input_type is a pre-send provider_invalid_request (§7, no request issued), NOT silently coerced to the default. Cohere's other input_type values (classification / clustering / image) ride the extras pass-through bag. embedding_types: [float] is sent EXPLICITLY (so the type-keyed response is guaranteed to carry the embeddings.float key the mapping reads); other precisions (int8 / uint8 / binary / base64) ride extras. dimensions -> Cohere's output_dimension (Matryoshka) when set, omitted otherwise. truncate: NONE is sent explicitly (fail-loud -- an over-length input errors rather than being silently truncated; the 400 maps to provider_invalid_request, the point where §8.4's embed half diverges from its rerank half). It parses {id, embeddings: {float: [[float, ...], ...]}, texts, meta: {billed_units: {input_tokens}}}: embeddings.float assembles to EmbeddingResponse.vectors IN INPUT ORDER (positional -- Cohere returns them in request order, NOT index-keyed like OpenAI / Jina data[]); meta.billed_units.input_tokens -> EmbeddingUsage.input_tokens (a record when the body surfaces it, else usage = null, never fabricated); top-level id -> response_id; Cohere echoes no model field, so EmbeddingResponse.model is the bound id. The §4 cross-impl invariants (one vector per input, uniform dimensionality) are enforced against the stitched result. It implements the MANDATORY batch chunk-and-stitch (§8.4 96-input cap / the §8 general embed rule): when len(input) exceeds 96 it splits into consecutive <=96 slices, issues one /v2/embed per slice with identical per-call params (only texts differs), concatenates the per-chunk embeddings.float in input order, sums input_tokens across chunks, and takes response_id from the first chunk; raw is the verbatim response object for a single call or the list of per-chunk objects for a chunked call (proposal 0096). One EmbeddingEvent per embed() call (the whole stitched call, not per chunk); input_type flows into EmbeddingEvent.request_params with absence-is-meaningful semantics (the caller's original query / document value, NOT the resolved search_document default the wire carries). Errors map per §8.4: 401/403 -> provider_authentication, 429 -> provider_rate_limit, 404 -> provider_invalid_model, 400/422 (malformed / over-length) -> provider_invalid_request, else -> provider_unavailable. genai_system = 'cohere'. Conformance fixtures 032-037 pass via the general wire-capture harness (cohere_embedding_provider block; request-origin asserted against base_url; the per-chunk expected_wire_request list + count for 037). The embedding observability + usage-nullable contract ride 0059b / 0093."
0 commit comments