Releases: axium-lab/llm-mock
Releases · axium-lab/llm-mock
Release list
v0.7.1
Fixed
- The Gemini resumable upload endpoint now also answers at
/gemini/upload/v1/files. Everything under the version segment was already served on bothv1betaandv1, butupload/sits ahead of that segment and was mounted on the preview version alone, so a client configured withapiVersion: "v1"— a supported setting now that the Interactions API and the rest of the surface are generally available on the stable version — got a404on file uploads only. The resumable session URL stays on whichever prefix the request came in on. - The
404for an unknown model names the version the request was made on instead of always claimingv1beta, matching what the real API reports to a caller on/v1.
Added
GET /azure/openai/deployments?api-version=andGET /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 reservedmissing-prefix still answersDeploymentNotFound.
v0.7.0
Added
GET /azure/openai/deployments?api-version=andGET /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 reservedmissing-prefix still answersDeploymentNotFound.
v0.6.1
Added
GET /healthnow also reports the runningversion, read frompackage.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 bothv1betaandv1: model catalog (GET /models,GET /models/{model}, emptyGET /tunedModels), validated against the official@google/genaiSDK. POST /gemini/v1beta/models/{model}:generateContent, withcandidates,finishReason,usageMetadatasplit by modality,modelVersionandresponseId, honoringcandidateCountand countingsystemInstructiontowards the prompt tokens.POST /gemini/v1beta/models/{model}:streamGenerateContent: SSE with?alt=sseand 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 bothv1beta1andv1: publisher model catalog underpublisherModels, served identically on the regionalprojects/{p}/locations/{l}/publishers/google/models/…path and the expresspublishers/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/v1one, 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 onanthropic-beta: files-api-2025-04-14— comma-separated or repeated, both accepted — while the?beta=truethe SDK appends is optional. A file you uploaded answers403on 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. MultipartFileinsrc/core/multipart.tsnow also carries the part's ownContent-Type, which Anthropic reports asmime_type(bare, with the charset the SDK'stoFileappends stripped).- Anthropic Message Batches, kept stateless by encoding the whole batch into its own
msgbatch_id: create, retrieve, results as JSONL keyed bycustom_id, cancel and delete, plus a list endpoint that is always empty. A batch isendedon arrival, so anx-llm-mock-batch-statusheader pinsin_progressorcancelingwhen 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,systemand the serialized tool declarations the same way/v1/messagesbuildsusage.input_tokens, so counting then sending reports the same number twice.- Anthropic streaming with named SSE events —
message_start→content_block_start/_delta/_stop→message_delta→message_stop, withinput_json_deltafor tool arguments and a separatesignature_deltaafter a thinking block. Both theevent:lines and themessage_stopterminator are required by the official client, making this the third SSE convention among the five providers. POST /anthropic/v1/messages: content blocks, a top-levelsystemcounted towardsinput_tokens,stop_reason/stop_sequence, tool use astool_useblocks with decodedinputandtoolu_ids,tool_choicein its object form, andthinkingblocks whose textdisplaydecides.max_tokensis required, as on the real API, and the conversation must open with the user.src/core/tools.tsnow also reads a tool declared withinput_schema, the fourth shape after OpenAI's nestedfunction, the Responses API's top-levelparameters, and Gemini'sfunctionDeclarations.- 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 throughx-api-key(withAuthorization: Bearerfor OAuth callers), a requiredanthropic-versionheader, and the doubly-wrapped error envelope whoserequest_idmatches thex-request-idresponse header. Managed Agents is deliberately out of scope. - Azure content filtering:
prompt_filter_resultson the response andcontent_filter_resultson every choice, plus anx-llm-mock-content-filterheader (<target>or<target>:<category>:<severity>) to pin the three documented failure paths — a blocked prompt as a400carrying its verdict ininnererror, a filtered completion asfinish_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_reasonon 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/v1surface, serving the same inference handlers with noapi-versionand no deployments, so a plainOpenAIclient reaches it with only abaseURLchange. Both surfaces return the same body for the same request. chat/completions(with SSE streaming and tool calls) andembeddingson Azure's deployment path, reusing the OpenAI provider's services since the payloads are OpenAI's verbatim. The handlers are mounted so the/openai/v1surface can share them.- Azure authentication through the
api-keyheader, withAuthorization: Beareraccepted 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 real404 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/interactionspath 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 insrc/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: aninstances/predictionsenvelope,outputDimensionalityset once per call in a siblingparametersobject, andstatistics.token_countin snake_case. Plus:countTokens, which reportstotalTokensalone. :generateContentand:streamGenerateContenton Gemini Enterprise, on both path shapes, reusing the shared generative core and adding thecreateTimethis platform stamps on every response. Tool calls,candidateCountand 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 anx-goog-api-key(express) failing as400 INVALID_ARGUMENT.AuthScheme.invalidKeyErrornow 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),embeddingsat Gemini's dimensions, andmodelsserving Gemini's catalog in OpenAI's list envelope. Only what Google actually exposes is mounted, so the Responses API and Files404there 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 forgemini-embedding-001, 768 for the older models) andoutputDimensionalitytruncation. :countTokens, accepting either a barecontentslist or a wholegenerateContentRequestso a system instruction and tool declarations are counted too.- Gemini Files API: the two-step resumable upload at
POST /gemini/upload/v1beta/files, plusGET /fileswithpageSize/pageToken,GET /files/{name}and an idempotentDELETE. - Stateless resumable uploads: the session metadata rides inside the
x-goog-upload-urlhanded to the client, and the file's metadata inside the name it is minted, soupload→getround-trips on any instance without a store.sha256Hashis the real digest of the bytes received. - Interactions API, Google's next-generation surface:
POST /gemini/v1beta/interactionswithsteps, snake_caseusageand tool calls,GET /interactions/{id}synthesized statelessly (with?stream=truereplay),POST /interactions/{id}/canceland an idempotentDELETE. - Interactions streaming:
interaction.created→step.start/step.delta/step.stop→interaction.completed, witharguments_deltapieces for a function call. - Gemini tool calls:
tools[].functionDeclarations[]andtoolConfig.functionCallingConfig(ANY,NONE,AUTO,allowedFunctionNames), emitted asfunctionCallparts whoseargsis a decoded object. Loops terminate on afunctionResponsepart, and both mock headers apply. - Gemini authentication via
x-goog-api-key, with the?key=query parameter andAuthorization: Beareralso 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, somodels/gemini-3.6-flash:generateContentreaches a handler instead of tripping over Express':parameter synt...
v0.6.0
Added
- Gemini (AI Studio) provider mounted at
/gemini, served on bothv1betaandv1: model catalog (GET /models,GET /models/{model}, emptyGET /tunedModels), validated against the official@google/genaiSDK. POST /gemini/v1beta/models/{model}:generateContent, withcandidates,finishReason,usageMetadatasplit by modality,modelVersionandresponseId, honoringcandidateCountand countingsystemInstructiontowards the prompt tokens.POST /gemini/v1beta/models/{model}:streamGenerateContent: SSE with?alt=sseand 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 bothv1beta1andv1: publisher model catalog underpublisherModels, served identically on the regionalprojects/{p}/locations/{l}/publishers/google/models/…path and the expresspublishers/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/v1one, 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 onanthropic-beta: files-api-2025-04-14— comma-separated or repeated, both accepted — while the?beta=truethe SDK appends is optional. A file you uploaded answers403on 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. MultipartFileinsrc/core/multipart.tsnow also carries the part's ownContent-Type, which Anthropic reports asmime_type(bare, with the charset the SDK'stoFileappends stripped).- Anthropic Message Batches, kept stateless by encoding the whole batch into its own
msgbatch_id: create, retrieve, results as JSONL keyed bycustom_id, cancel and delete, plus a list endpoint that is always empty. A batch isendedon arrival, so anx-llm-mock-batch-statusheader pinsin_progressorcancelingwhen 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,systemand the serialized tool declarations the same way/v1/messagesbuildsusage.input_tokens, so counting then sending reports the same number twice.- Anthropic streaming with named SSE events —
message_start→content_block_start/_delta/_stop→message_delta→message_stop, withinput_json_deltafor tool arguments and a separatesignature_deltaafter a thinking block. Both theevent:lines and themessage_stopterminator are required by the official client, making this the third SSE convention among the five providers. POST /anthropic/v1/messages: content blocks, a top-levelsystemcounted towardsinput_tokens,stop_reason/stop_sequence, tool use astool_useblocks with decodedinputandtoolu_ids,tool_choicein its object form, andthinkingblocks whose textdisplaydecides.max_tokensis required, as on the real API, and the conversation must open with the user.src/core/tools.tsnow also reads a tool declared withinput_schema, the fourth shape after OpenAI's nestedfunction, the Responses API's top-levelparameters, and Gemini'sfunctionDeclarations.- 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 throughx-api-key(withAuthorization: Bearerfor OAuth callers), a requiredanthropic-versionheader, and the doubly-wrapped error envelope whoserequest_idmatches thex-request-idresponse header. Managed Agents is deliberately out of scope. - Azure content filtering:
prompt_filter_resultson the response andcontent_filter_resultson every choice, plus anx-llm-mock-content-filterheader (<target>or<target>:<category>:<severity>) to pin the three documented failure paths — a blocked prompt as a400carrying its verdict ininnererror, a filtered completion asfinish_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_reasonon 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/v1surface, serving the same inference handlers with noapi-versionand no deployments, so a plainOpenAIclient reaches it with only abaseURLchange. Both surfaces return the same body for the same request. chat/completions(with SSE streaming and tool calls) andembeddingson Azure's deployment path, reusing the OpenAI provider's services since the payloads are OpenAI's verbatim. The handlers are mounted so the/openai/v1surface can share them.- Azure authentication through the
api-keyheader, withAuthorization: Beareraccepted 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 real404 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/interactionspath 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 insrc/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: aninstances/predictionsenvelope,outputDimensionalityset once per call in a siblingparametersobject, andstatistics.token_countin snake_case. Plus:countTokens, which reportstotalTokensalone. :generateContentand:streamGenerateContenton Gemini Enterprise, on both path shapes, reusing the shared generative core and adding thecreateTimethis platform stamps on every response. Tool calls,candidateCountand 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 anx-goog-api-key(express) failing as400 INVALID_ARGUMENT.AuthScheme.invalidKeyErrornow 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),embeddingsat Gemini's dimensions, andmodelsserving Gemini's catalog in OpenAI's list envelope. Only what Google actually exposes is mounted, so the Responses API and Files404there 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 forgemini-embedding-001, 768 for the older models) andoutputDimensionalitytruncation. :countTokens, accepting either a barecontentslist or a wholegenerateContentRequestso a system instruction and tool declarations are counted too.- Gemini Files API: the two-step resumable upload at
POST /gemini/upload/v1beta/files, plusGET /fileswithpageSize/pageToken,GET /files/{name}and an idempotentDELETE. - Stateless resumable uploads: the session metadata rides inside the
x-goog-upload-urlhanded to the client, and the file's metadata inside the name it is minted, soupload→getround-trips on any instance without a store.sha256Hashis the real digest of the bytes received. - Interactions API, Google's next-generation surface:
POST /gemini/v1beta/interactionswithsteps, snake_caseusageand tool calls,GET /interactions/{id}synthesized statelessly (with?stream=truereplay),POST /interactions/{id}/canceland an idempotentDELETE. - Interactions streaming:
interaction.created→step.start/step.delta/step.stop→interaction.completed, witharguments_deltapieces for a function call. - Gemini tool calls:
tools[].functionDeclarations[]andtoolConfig.functionCallingConfig(ANY,NONE,AUTO,allowedFunctionNames), emitted asfunctionCallparts whoseargsis a decoded object. Loops terminate on afunctionResponsepart, and both mock headers apply. - Gemini authentication via
x-goog-api-key, with the?key=query parameter andAuthorization: Beareralso 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, somodels/gemini-3.6-flash:generateContentreaches 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 ...
v0.5.0
Files and Uploads API
Full Changelog: v0.2...v0.4.0
v0.3.0
feat: update documentation to include files and uploads API details
v0.2: Merge pull request #1 from axium-lab/feat/multi-provider-structure
Feat/multi provider structure
v0.1.0
feat: add CI and release workflows for Docker image management