Skip to content

Releases: axium-lab/llm-mock

v0.7.1

Choose a tag to compare

@github-actions github-actions released this 10 Aug 08:07

Fixed

  • The Gemini resumable upload endpoint now also answers at /gemini/upload/v1/files. Everything under the version segment was already served on both v1beta and v1, but upload/ sits ahead of that segment and was mounted on the preview version alone, so a client configured with apiVersion: "v1" — a supported setting now that the Interactions API and the rest of the surface are generally available on the stable version — got a 404 on file uploads only. The resumable session URL stays on whichever prefix the request came in on.
  • The 404 for an unknown model names the version the request was made on instead of always claiming v1beta, matching what the real API reports to a caller on /v1.

Added

  • GET /azure/openai/deployments?api-version= and GET /azure/openai/deployments/{deployment}, the legacy data-plane listing, so a client can discover deployment names without leaving the endpoint it is pointed at — managing deployments on the real service moved to ARM, which a mock behind a single base URL cannot represent. One deployment per catalog model, named after the model it points at; any other name is described too, since the mock accepts any of them for inference. The reserved missing- prefix still answers DeploymentNotFound.

v0.7.0

Choose a tag to compare

@github-actions github-actions released this 02 Aug 21:27

Added

  • GET /azure/openai/deployments?api-version= and GET /azure/openai/deployments/{deployment}, the legacy data-plane listing, so a client can discover deployment names without leaving the endpoint it is pointed at — managing deployments on the real service moved to ARM, which a mock behind a single base URL cannot represent. One deployment per catalog model, named after the model it points at; any other name is described too, since the mock accepts any of them for inference. The reserved missing- prefix still answers DeploymentNotFound.

v0.6.1

Choose a tag to compare

@github-actions github-actions released this 02 Aug 18:56

Added

  • GET /health now also reports the running version, read from package.json, so a deployed container can be matched against the release and image tag it was built from.
  • Gemini (AI Studio) provider mounted at /gemini, served on both v1beta and v1: model catalog (GET /models, GET /models/{model}, empty GET /tunedModels), validated against the official @google/genai SDK.
  • POST /gemini/v1beta/models/{model}:generateContent, with candidates, finishReason, usageMetadata split by modality, modelVersion and responseId, honoring candidateCount and counting systemInstruction towards the prompt tokens.
  • POST /gemini/v1beta/models/{model}:streamGenerateContent: SSE with ?alt=sse and a streamed JSON array without it. No [DONE] sentinel, which the Gemini SDK would reject as an incomplete JSON segment.
  • Gemini Enterprise provider (the platform formerly called Vertex AI) mounted at /gemini-enterprise, on both v1beta1 and v1: publisher model catalog under publisherModels, served identically on the regional projects/{p}/locations/{l}/publishers/google/models/… path and the express publishers/google/models/… one.
  • Azure OpenAI provider mounted at /azure/openai, serving both of Azure's surfaces: the classic deployment-based one (/openai/deployments/{deployment}/…?api-version=, with the version required on every call) and the newer /openai/v1 one, which is OpenAI's contract verbatim. Model catalog on both.
  • Anthropic Files (beta): multipart upload, metadata, listing filterable by scope_id, download and delete, with the file's metadata carried inside the id it is minted so an upload round-trips without a store. Every call is gated on anthropic-beta: files-api-2025-04-14 — comma-separated or repeated, both accepted — while the ?beta=true the SDK appends is optional. A file you uploaded answers 403 on download, as on the real API; the simulated catalog carries two API-produced files so the download path is testable.
  • A message whose only content is a file attachment now echoes the attachment (Echo: [file_…]) instead of falling through to the generic greeting, so a test can assert the reference arrived.
  • MultipartFile in src/core/multipart.ts now also carries the part's own Content-Type, which Anthropic reports as mime_type (bare, with the charset the SDK's toFile appends stripped).
  • Anthropic Message Batches, kept stateless by encoding the whole batch into its own msgbatch_ id: create, retrieve, results as JSONL keyed by custom_id, cancel and delete, plus a list endpoint that is always empty. A batch is ended on arrival, so an x-llm-mock-batch-status header pins in_progress or canceling when a test needs the polling branch. Encoding bounds the size: past ~1600 characters of id, creation fails loudly instead of dropping requests.
  • POST /anthropic/v1/messages/count_tokens, summing messages, system and the serialized tool declarations the same way /v1/messages builds usage.input_tokens, so counting then sending reports the same number twice.
  • Anthropic streaming with named SSE events — message_startcontent_block_start/_delta/_stopmessage_deltamessage_stop, with input_json_delta for tool arguments and a separate signature_delta after a thinking block. Both the event: lines and the message_stop terminator are required by the official client, making this the third SSE convention among the five providers.
  • POST /anthropic/v1/messages: content blocks, a top-level system counted towards input_tokens, stop_reason/stop_sequence, tool use as tool_use blocks with decoded input and toolu_ ids, tool_choice in its object form, and thinking blocks whose text display decides. max_tokens is required, as on the real API, and the conversation must open with the user.
  • src/core/tools.ts now also reads a tool declared with input_schema, the fourth shape after OpenAI's nested function, the Responses API's top-level parameters, and Gemini's functionDeclarations.
  • Anthropic provider mounted at /anthropic, validated against the official @anthropic-ai/sdk: model catalog with cursor pagination (after_id/before_id) and the real capability tree, authentication through x-api-key (with Authorization: Bearer for OAuth callers), a required anthropic-version header, and the doubly-wrapped error envelope whose request_id matches the x-request-id response header. Managed Agents is deliberately out of scope.
  • Azure content filtering: prompt_filter_results on the response and content_filter_results on every choice, plus an x-llm-mock-content-filter header (<target> or <target>:<category>:<severity>) to pin the three documented failure paths — a blocked prompt as a 400 carrying its verdict in innererror, a filtered completion as finish_reason: "content_filter" with null content, and the "filter did not run" case. Streamed responses open with a choice-less chunk carrying the prompt's verdict, as Azure's do.
  • finish_reason on the shared OpenAI chat types now admits "content_filter", which is part of that contract even though the OpenAI provider never produces it.
  • Azure's /openai/v1 surface, serving the same inference handlers with no api-version and no deployments, so a plain OpenAI client reaches it with only a baseURL change. Both surfaces return the same body for the same request.
  • chat/completions (with SSE streaming and tool calls) and embeddings on Azure's deployment path, reusing the OpenAI provider's services since the payloads are OpenAI's verbatim. The handlers are mounted so the /openai/v1 surface can share them.
  • Azure authentication through the api-key header, with Authorization: Bearer accepted for Entra ID callers. A missing credential answers in the API gateway's flatter {statusCode, message} shape; an invalid one in the wrapped {error:{code, message}} envelope.
  • Any deployment name resolves, except those prefixed missing-, which return the real 404 DeploymentNotFound — the same reserved-name convention already used for files on OpenAI and Gemini.
  • Interactions API on Gemini Enterprise, reachable on both the express /v1beta1/interactions path and the regional one, where the SDK percent-encodes the whole version-project-location component into a single path segment that a literal version mount cannot match. The router itself now lives in src/providers/google-shared/routes/interactions.ts, shared verbatim by both Google providers.
  • Gemini Enterprise embeddings through :predict, the generic prediction endpoint this platform uses instead of AI Studio's :embedContent: an instances/predictions envelope, outputDimensionality set once per call in a sibling parameters object, and statistics.token_count in snake_case. Plus :countTokens, which reports totalTokens alone.
  • :generateContent and :streamGenerateContent on Gemini Enterprise, on both path shapes, reusing the shared generative core and adding the createTime this platform stamps on every response. Tool calls, candidateCount and the mock headers all carry over.
  • Dual authentication for that provider, validated against the same key set: an OAuth bearer token (regional) failing as 401 UNAUTHENTICATED, and an x-goog-api-key (express) failing as 400 INVALID_ARGUMENT. AuthScheme.invalidKeyError now receives the request so a provider can tell its transports apart.
  • Gemini's OpenAI-compatibility layer at /gemini/v1beta/openai: chat/completions (streaming and tool calls included), embeddings at Gemini's dimensions, and models serving Gemini's catalog in OpenAI's list envelope. Only what Google actually exposes is mounted, so the Responses API and Files 404 there as they do against the real service.
  • Gemini embeddings: :batchEmbedContents (what the SDK calls even for a single input) and the singular :embedContent, with hash-seeded unit vectors, per-model native dimensions (3072 for gemini-embedding-001, 768 for the older models) and outputDimensionality truncation.
  • :countTokens, accepting either a bare contents list or a whole generateContentRequest so a system instruction and tool declarations are counted too.
  • Gemini Files API: the two-step resumable upload at POST /gemini/upload/v1beta/files, plus GET /files with pageSize/pageToken, GET /files/{name} and an idempotent DELETE.
  • Stateless resumable uploads: the session metadata rides inside the x-goog-upload-url handed to the client, and the file's metadata inside the name it is minted, so uploadget round-trips on any instance without a store. sha256Hash is the real digest of the bytes received.
  • Interactions API, Google's next-generation surface: POST /gemini/v1beta/interactions with steps, snake_case usage and tool calls, GET /interactions/{id} synthesized statelessly (with ?stream=true replay), POST /interactions/{id}/cancel and an idempotent DELETE.
  • Interactions streaming: interaction.createdstep.start / step.delta / step.stopinteraction.completed, with arguments_delta pieces for a function call.
  • Gemini tool calls: tools[].functionDeclarations[] and toolConfig.functionCallingConfig (ANY, NONE, AUTO, allowedFunctionNames), emitted as functionCall parts whose args is a decoded object. Loops terminate on a functionResponse part, and both mock headers apply.
  • Gemini authentication via x-goog-api-key, with the ?key= query parameter and Authorization: Bearer also accepted. The API key set is shared across providers.
  • Gemini error envelopes: google.rpc.Status (code/message/status/details) on the classic surface, and the flatter Interactions-style envelope under /interactions.
  • Routing for Google's {resource}:{method} custom methods, so models/gemini-3.6-flash:generateContent reaches a handler instead of tripping over Express' : parameter synt...
Read more

v0.6.0

Choose a tag to compare

@github-actions github-actions released this 02 Aug 18:45

Added

  • Gemini (AI Studio) provider mounted at /gemini, served on both v1beta and v1: model catalog (GET /models, GET /models/{model}, empty GET /tunedModels), validated against the official @google/genai SDK.
  • POST /gemini/v1beta/models/{model}:generateContent, with candidates, finishReason, usageMetadata split by modality, modelVersion and responseId, honoring candidateCount and counting systemInstruction towards the prompt tokens.
  • POST /gemini/v1beta/models/{model}:streamGenerateContent: SSE with ?alt=sse and a streamed JSON array without it. No [DONE] sentinel, which the Gemini SDK would reject as an incomplete JSON segment.
  • Gemini Enterprise provider (the platform formerly called Vertex AI) mounted at /gemini-enterprise, on both v1beta1 and v1: publisher model catalog under publisherModels, served identically on the regional projects/{p}/locations/{l}/publishers/google/models/… path and the express publishers/google/models/… one.
  • Azure OpenAI provider mounted at /azure/openai, serving both of Azure's surfaces: the classic deployment-based one (/openai/deployments/{deployment}/…?api-version=, with the version required on every call) and the newer /openai/v1 one, which is OpenAI's contract verbatim. Model catalog on both.
  • Anthropic Files (beta): multipart upload, metadata, listing filterable by scope_id, download and delete, with the file's metadata carried inside the id it is minted so an upload round-trips without a store. Every call is gated on anthropic-beta: files-api-2025-04-14 — comma-separated or repeated, both accepted — while the ?beta=true the SDK appends is optional. A file you uploaded answers 403 on download, as on the real API; the simulated catalog carries two API-produced files so the download path is testable.
  • A message whose only content is a file attachment now echoes the attachment (Echo: [file_…]) instead of falling through to the generic greeting, so a test can assert the reference arrived.
  • MultipartFile in src/core/multipart.ts now also carries the part's own Content-Type, which Anthropic reports as mime_type (bare, with the charset the SDK's toFile appends stripped).
  • Anthropic Message Batches, kept stateless by encoding the whole batch into its own msgbatch_ id: create, retrieve, results as JSONL keyed by custom_id, cancel and delete, plus a list endpoint that is always empty. A batch is ended on arrival, so an x-llm-mock-batch-status header pins in_progress or canceling when a test needs the polling branch. Encoding bounds the size: past ~1600 characters of id, creation fails loudly instead of dropping requests.
  • POST /anthropic/v1/messages/count_tokens, summing messages, system and the serialized tool declarations the same way /v1/messages builds usage.input_tokens, so counting then sending reports the same number twice.
  • Anthropic streaming with named SSE events — message_startcontent_block_start/_delta/_stopmessage_deltamessage_stop, with input_json_delta for tool arguments and a separate signature_delta after a thinking block. Both the event: lines and the message_stop terminator are required by the official client, making this the third SSE convention among the five providers.
  • POST /anthropic/v1/messages: content blocks, a top-level system counted towards input_tokens, stop_reason/stop_sequence, tool use as tool_use blocks with decoded input and toolu_ ids, tool_choice in its object form, and thinking blocks whose text display decides. max_tokens is required, as on the real API, and the conversation must open with the user.
  • src/core/tools.ts now also reads a tool declared with input_schema, the fourth shape after OpenAI's nested function, the Responses API's top-level parameters, and Gemini's functionDeclarations.
  • Anthropic provider mounted at /anthropic, validated against the official @anthropic-ai/sdk: model catalog with cursor pagination (after_id/before_id) and the real capability tree, authentication through x-api-key (with Authorization: Bearer for OAuth callers), a required anthropic-version header, and the doubly-wrapped error envelope whose request_id matches the x-request-id response header. Managed Agents is deliberately out of scope.
  • Azure content filtering: prompt_filter_results on the response and content_filter_results on every choice, plus an x-llm-mock-content-filter header (<target> or <target>:<category>:<severity>) to pin the three documented failure paths — a blocked prompt as a 400 carrying its verdict in innererror, a filtered completion as finish_reason: "content_filter" with null content, and the "filter did not run" case. Streamed responses open with a choice-less chunk carrying the prompt's verdict, as Azure's do.
  • finish_reason on the shared OpenAI chat types now admits "content_filter", which is part of that contract even though the OpenAI provider never produces it.
  • Azure's /openai/v1 surface, serving the same inference handlers with no api-version and no deployments, so a plain OpenAI client reaches it with only a baseURL change. Both surfaces return the same body for the same request.
  • chat/completions (with SSE streaming and tool calls) and embeddings on Azure's deployment path, reusing the OpenAI provider's services since the payloads are OpenAI's verbatim. The handlers are mounted so the /openai/v1 surface can share them.
  • Azure authentication through the api-key header, with Authorization: Bearer accepted for Entra ID callers. A missing credential answers in the API gateway's flatter {statusCode, message} shape; an invalid one in the wrapped {error:{code, message}} envelope.
  • Any deployment name resolves, except those prefixed missing-, which return the real 404 DeploymentNotFound — the same reserved-name convention already used for files on OpenAI and Gemini.
  • Interactions API on Gemini Enterprise, reachable on both the express /v1beta1/interactions path and the regional one, where the SDK percent-encodes the whole version-project-location component into a single path segment that a literal version mount cannot match. The router itself now lives in src/providers/google-shared/routes/interactions.ts, shared verbatim by both Google providers.
  • Gemini Enterprise embeddings through :predict, the generic prediction endpoint this platform uses instead of AI Studio's :embedContent: an instances/predictions envelope, outputDimensionality set once per call in a sibling parameters object, and statistics.token_count in snake_case. Plus :countTokens, which reports totalTokens alone.
  • :generateContent and :streamGenerateContent on Gemini Enterprise, on both path shapes, reusing the shared generative core and adding the createTime this platform stamps on every response. Tool calls, candidateCount and the mock headers all carry over.
  • Dual authentication for that provider, validated against the same key set: an OAuth bearer token (regional) failing as 401 UNAUTHENTICATED, and an x-goog-api-key (express) failing as 400 INVALID_ARGUMENT. AuthScheme.invalidKeyError now receives the request so a provider can tell its transports apart.
  • Gemini's OpenAI-compatibility layer at /gemini/v1beta/openai: chat/completions (streaming and tool calls included), embeddings at Gemini's dimensions, and models serving Gemini's catalog in OpenAI's list envelope. Only what Google actually exposes is mounted, so the Responses API and Files 404 there as they do against the real service.
  • Gemini embeddings: :batchEmbedContents (what the SDK calls even for a single input) and the singular :embedContent, with hash-seeded unit vectors, per-model native dimensions (3072 for gemini-embedding-001, 768 for the older models) and outputDimensionality truncation.
  • :countTokens, accepting either a bare contents list or a whole generateContentRequest so a system instruction and tool declarations are counted too.
  • Gemini Files API: the two-step resumable upload at POST /gemini/upload/v1beta/files, plus GET /files with pageSize/pageToken, GET /files/{name} and an idempotent DELETE.
  • Stateless resumable uploads: the session metadata rides inside the x-goog-upload-url handed to the client, and the file's metadata inside the name it is minted, so uploadget round-trips on any instance without a store. sha256Hash is the real digest of the bytes received.
  • Interactions API, Google's next-generation surface: POST /gemini/v1beta/interactions with steps, snake_case usage and tool calls, GET /interactions/{id} synthesized statelessly (with ?stream=true replay), POST /interactions/{id}/cancel and an idempotent DELETE.
  • Interactions streaming: interaction.createdstep.start / step.delta / step.stopinteraction.completed, with arguments_delta pieces for a function call.
  • Gemini tool calls: tools[].functionDeclarations[] and toolConfig.functionCallingConfig (ANY, NONE, AUTO, allowedFunctionNames), emitted as functionCall parts whose args is a decoded object. Loops terminate on a functionResponse part, and both mock headers apply.
  • Gemini authentication via x-goog-api-key, with the ?key= query parameter and Authorization: Bearer also accepted. The API key set is shared across providers.
  • Gemini error envelopes: google.rpc.Status (code/message/status/details) on the classic surface, and the flatter Interactions-style envelope under /interactions.
  • Routing for Google's {resource}:{method} custom methods, so models/gemini-3.6-flash:generateContent reaches a handler instead of tripping over Express' : parameter syntax.
  • Tool calling on both Chat Completions and the Responses API: tool_choice: "required" or a named function returns real tool calls, with arguments synthesized from the ...
Read more

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 01 Aug 08:17

Full Changelog: v0.3...v0.5.0

Files and Uploads API

Choose a tag to compare

@LuisPerisVidal LuisPerisVidal released this 01 Aug 06:58

Full Changelog: v0.2...v0.4.0

v0.3.0

Choose a tag to compare

@LuisPerisVidal LuisPerisVidal released this 31 Jul 22:56
feat: update documentation to include files and uploads API details

v0.2: Merge pull request #1 from axium-lab/feat/multi-provider-structure

Choose a tag to compare

@LuisPerisVidal LuisPerisVidal released this 22 Jul 15:29
89086d1
Feat/multi provider structure

v0.1.0

Choose a tag to compare

@LuisPerisVidal LuisPerisVidal released this 22 Jul 09:03
feat: add CI and release workflows for Docker image management