@copilotkit/aimock 1.43.0 → 1.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +15 -0
- package/README.md +8 -2
- package/dist/agui-handler.d.ts.map +1 -1
- package/dist/agui-types.d.ts.map +1 -1
- package/dist/bedrock-converse.cjs +32 -4
- package/dist/bedrock-converse.cjs.map +1 -1
- package/dist/bedrock-converse.d.cts.map +1 -1
- package/dist/bedrock-converse.d.ts.map +1 -1
- package/dist/bedrock-converse.js +32 -4
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/chaos.cjs +8 -5
- package/dist/chaos.cjs.map +1 -1
- package/dist/chaos.d.cts.map +1 -1
- package/dist/chaos.d.ts.map +1 -1
- package/dist/chaos.js +9 -6
- package/dist/chaos.js.map +1 -1
- package/dist/cli.cjs +89 -7
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +91 -9
- package/dist/cli.js.map +1 -1
- package/dist/cohere.cjs +27 -1
- package/dist/cohere.cjs.map +1 -1
- package/dist/cohere.js +27 -1
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.cjs +1 -1
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/config-loader.js +1 -1
- package/dist/constants.cjs +8 -0
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +6 -2
- package/dist/constants.d.ts +6 -2
- package/dist/constants.js +8 -1
- package/dist/constants.js.map +1 -1
- package/dist/echo-text.cjs +65 -0
- package/dist/echo-text.cjs.map +1 -0
- package/dist/echo-text.d.cts +22 -0
- package/dist/echo-text.d.cts.map +1 -0
- package/dist/echo-text.d.ts +22 -0
- package/dist/echo-text.d.ts.map +1 -0
- package/dist/echo-text.js +65 -0
- package/dist/echo-text.js.map +1 -0
- package/dist/embeddings.cjs +18 -1
- package/dist/embeddings.cjs.map +1 -1
- package/dist/embeddings.d.cts +1 -1
- package/dist/embeddings.d.cts.map +1 -1
- package/dist/embeddings.d.ts +1 -1
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/embeddings.js +18 -1
- package/dist/embeddings.js.map +1 -1
- package/dist/files.cjs +9 -11
- package/dist/files.cjs.map +1 -1
- package/dist/files.d.cts.map +1 -1
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +10 -13
- package/dist/files.js.map +1 -1
- package/dist/fine-tuning.cjs +1 -19
- package/dist/fine-tuning.cjs.map +1 -1
- package/dist/fine-tuning.d.cts.map +1 -1
- package/dist/fine-tuning.d.ts.map +1 -1
- package/dist/fine-tuning.js +2 -20
- package/dist/fine-tuning.js.map +1 -1
- package/dist/fixture-loader-services.cjs +116 -0
- package/dist/fixture-loader-services.cjs.map +1 -0
- package/dist/fixture-loader-services.d.cts +48 -0
- package/dist/fixture-loader-services.d.cts.map +1 -0
- package/dist/fixture-loader-services.d.ts +48 -0
- package/dist/fixture-loader-services.d.ts.map +1 -0
- package/dist/fixture-loader-services.js +114 -0
- package/dist/fixture-loader-services.js.map +1 -0
- package/dist/fixture-loader.cjs +172 -11
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts +95 -1
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts +95 -1
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +168 -12
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini-embeddings.cjs +42 -2
- package/dist/gemini-embeddings.cjs.map +1 -1
- package/dist/gemini-embeddings.js +43 -3
- package/dist/gemini-embeddings.js.map +1 -1
- package/dist/gemini-interactions.cjs +38 -0
- package/dist/gemini-interactions.cjs.map +1 -1
- package/dist/gemini-interactions.d.cts.map +1 -1
- package/dist/gemini-interactions.d.ts.map +1 -1
- package/dist/gemini-interactions.js +38 -0
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +8 -1
- package/dist/gemini.cjs.map +1 -1
- package/dist/gemini.d.cts.map +1 -1
- package/dist/gemini.d.ts.map +1 -1
- package/dist/gemini.js +8 -1
- package/dist/gemini.js.map +1 -1
- package/dist/helpers.cjs +276 -5
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +275 -7
- package/dist/helpers.js.map +1 -1
- package/dist/images.cjs +3 -2
- package/dist/images.cjs.map +1 -1
- package/dist/images.js +3 -2
- package/dist/images.js.map +1 -1
- package/dist/index.cjs +30 -1
- package/dist/index.d.cts +8 -3
- package/dist/index.d.ts +8 -3
- package/dist/index.js +9 -3
- package/dist/jest.cjs +15 -10
- package/dist/jest.cjs.map +1 -1
- package/dist/jest.d.cts +4 -2
- package/dist/jest.d.cts.map +1 -1
- package/dist/jest.d.ts +4 -2
- package/dist/jest.d.ts.map +1 -1
- package/dist/jest.js +16 -11
- package/dist/jest.js.map +1 -1
- package/dist/journal.cjs +20 -0
- package/dist/journal.cjs.map +1 -1
- package/dist/journal.d.cts +16 -0
- package/dist/journal.d.cts.map +1 -1
- package/dist/journal.d.ts +16 -0
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +20 -0
- package/dist/journal.js.map +1 -1
- package/dist/llmock.cjs +82 -10
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts +35 -0
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts +35 -0
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +83 -11
- package/dist/llmock.js.map +1 -1
- package/dist/mcp-fakes-mount.cjs +216 -0
- package/dist/mcp-fakes-mount.cjs.map +1 -0
- package/dist/mcp-fakes-mount.js +210 -0
- package/dist/mcp-fakes-mount.js.map +1 -0
- package/dist/mcp-fakes.cjs +1619 -0
- package/dist/mcp-fakes.cjs.map +1 -0
- package/dist/mcp-fakes.d.cts +101 -0
- package/dist/mcp-fakes.d.cts.map +1 -0
- package/dist/mcp-fakes.d.ts +101 -0
- package/dist/mcp-fakes.d.ts.map +1 -0
- package/dist/mcp-fakes.js +1614 -0
- package/dist/mcp-fakes.js.map +1 -0
- package/dist/mcp-handler.cjs +331 -17
- package/dist/mcp-handler.cjs.map +1 -1
- package/dist/mcp-handler.js +330 -18
- package/dist/mcp-handler.js.map +1 -1
- package/dist/mcp-mock.cjs +217 -49
- package/dist/mcp-mock.cjs.map +1 -1
- package/dist/mcp-mock.d.cts +66 -1
- package/dist/mcp-mock.d.cts.map +1 -1
- package/dist/mcp-mock.d.ts +66 -1
- package/dist/mcp-mock.d.ts.map +1 -1
- package/dist/mcp-mock.js +218 -50
- package/dist/mcp-mock.js.map +1 -1
- package/dist/mcp-stub.cjs +5 -1
- package/dist/mcp-stub.d.cts +3 -1
- package/dist/mcp-stub.d.ts +3 -1
- package/dist/mcp-stub.js +3 -1
- package/dist/mcp-types.d.cts +6 -0
- package/dist/mcp-types.d.ts +6 -0
- package/dist/message-text.cjs +192 -0
- package/dist/message-text.cjs.map +1 -0
- package/dist/message-text.d.cts +31 -0
- package/dist/message-text.d.cts.map +1 -0
- package/dist/message-text.d.ts +31 -0
- package/dist/message-text.d.ts.map +1 -0
- package/dist/message-text.js +184 -0
- package/dist/message-text.js.map +1 -0
- package/dist/messages.cjs +38 -0
- package/dist/messages.cjs.map +1 -1
- package/dist/messages.js +38 -0
- package/dist/messages.js.map +1 -1
- package/dist/metrics.cjs +46 -66
- package/dist/metrics.cjs.map +1 -1
- package/dist/metrics.d.cts +14 -6
- package/dist/metrics.d.cts.map +1 -1
- package/dist/metrics.d.ts +14 -6
- package/dist/metrics.d.ts.map +1 -1
- package/dist/metrics.js +32 -40
- package/dist/metrics.js.map +1 -1
- package/dist/ollama.cjs +5 -1
- package/dist/ollama.cjs.map +1 -1
- package/dist/ollama.d.cts.map +1 -1
- package/dist/ollama.d.ts.map +1 -1
- package/dist/ollama.js +5 -1
- package/dist/ollama.js.map +1 -1
- package/dist/openapi.cjs +3405 -0
- package/dist/openapi.cjs.map +1 -0
- package/dist/openapi.d.cts +30 -0
- package/dist/openapi.d.cts.map +1 -0
- package/dist/openapi.d.ts +30 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +3404 -0
- package/dist/openapi.js.map +1 -0
- package/dist/openrouter-video.cjs +4 -4
- package/dist/openrouter-video.cjs.map +1 -1
- package/dist/openrouter-video.d.cts.map +1 -1
- package/dist/openrouter-video.d.ts.map +1 -1
- package/dist/openrouter-video.js +4 -4
- package/dist/openrouter-video.js.map +1 -1
- package/dist/recorder.cjs +9 -1
- package/dist/recorder.cjs.map +1 -1
- package/dist/recorder.d.cts.map +1 -1
- package/dist/recorder.d.ts.map +1 -1
- package/dist/recorder.js +9 -1
- package/dist/recorder.js.map +1 -1
- package/dist/responses.cjs +43 -4
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.d.cts.map +1 -1
- package/dist/responses.d.ts.map +1 -1
- package/dist/responses.js +43 -4
- package/dist/responses.js.map +1 -1
- package/dist/route-registry.cjs +951 -0
- package/dist/route-registry.cjs.map +1 -0
- package/dist/route-registry.d.cts +33 -0
- package/dist/route-registry.d.cts.map +1 -0
- package/dist/route-registry.d.ts +33 -0
- package/dist/route-registry.d.ts.map +1 -0
- package/dist/route-registry.js +866 -0
- package/dist/route-registry.js.map +1 -0
- package/dist/router.cjs +1 -1
- package/dist/router.cjs.map +1 -1
- package/dist/router.js +1 -1
- package/dist/router.js.map +1 -1
- package/dist/server.cjs +590 -191
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +4 -1
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts +4 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +498 -99
- package/dist/server.js.map +1 -1
- package/dist/speech.cjs +4 -2
- package/dist/speech.cjs.map +1 -1
- package/dist/speech.js +4 -2
- package/dist/speech.js.map +1 -1
- package/dist/suite.cjs +1 -1
- package/dist/suite.js +1 -1
- package/dist/transcription.cjs +2 -2
- package/dist/transcription.cjs.map +1 -1
- package/dist/transcription.d.cts.map +1 -1
- package/dist/transcription.d.ts.map +1 -1
- package/dist/transcription.js +2 -2
- package/dist/transcription.js.map +1 -1
- package/dist/types.cjs +18 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +202 -2
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +202 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +17 -0
- package/dist/types.js.map +1 -0
- package/dist/validate-cli.cjs +89 -31
- package/dist/validate-cli.cjs.map +1 -1
- package/dist/validate-cli.js +91 -33
- package/dist/validate-cli.js.map +1 -1
- package/dist/vector-stores.cjs +1180 -0
- package/dist/vector-stores.cjs.map +1 -0
- package/dist/vector-stores.d.cts +106 -0
- package/dist/vector-stores.d.cts.map +1 -0
- package/dist/vector-stores.d.ts +106 -0
- package/dist/vector-stores.d.ts.map +1 -0
- package/dist/vector-stores.js +1164 -0
- package/dist/vector-stores.js.map +1 -0
- package/dist/vector-types.d.cts.map +1 -1
- package/dist/vector-types.d.ts.map +1 -1
- package/dist/video.cjs +19 -0
- package/dist/video.cjs.map +1 -1
- package/dist/video.d.cts.map +1 -1
- package/dist/video.d.ts.map +1 -1
- package/dist/video.js +19 -0
- package/dist/video.js.map +1 -1
- package/dist/vitest.cjs +15 -10
- package/dist/vitest.cjs.map +1 -1
- package/dist/vitest.d.cts +4 -2
- package/dist/vitest.d.cts.map +1 -1
- package/dist/vitest.d.ts +4 -2
- package/dist/vitest.d.ts.map +1 -1
- package/dist/vitest.js +16 -11
- package/dist/vitest.js.map +1 -1
- package/dist/ws-gemini-live.d.ts +2 -2
- package/dist/ws-realtime.d.ts +2 -2
- package/dist/ws-responses.cjs +14 -2
- package/dist/ws-responses.cjs.map +1 -1
- package/dist/ws-responses.js +14 -2
- package/dist/ws-responses.js.map +1 -1
- package/package.json +4 -1
package/dist/server.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { DEFAULT_TEST_ID } from "./constants.js";
|
|
2
|
+
import { build, msg, quote } from "./message-text.js";
|
|
2
3
|
import { markAuthenticatedRequest, resolveInboundAuth, validateRequestApiKey, writeApiKeyHttpRejection, writeApiKeyUpgradeRejection } from "./api-key-auth.js";
|
|
3
|
-
import { RequestBodyTooLargeError, buildContentWithToolCallsChunks, buildContentWithToolCallsCompletion, buildTextChunks, buildTextCompletion, buildToolCallChunks, buildToolCallCompletion, buildUsageChunk, describeMatch, extractOverrides, flattenHeaders, getContext, getTestId, isAudioResponse, isContentWithToolCallsResponse, isEmbeddingResponse, isErrorResponse, isImageResponse, isJSONResponse, isJsonObject, isResponseFactory, isTextResponse, isToolCallResponse, isTranscriptionResponse, isVideoResponse, markMintedRequestId, readBody, readBodyBufferBounded, resolveFixtureBlockOutcome, resolveReasoningForModel, resolveRequestId, resolveResponse, resolveStrictMode, resolveTestId, resolveUsage, serializeErrorResponse, strictNoMatchLogLine, strictNoMatchMessage, strictOverrideField, wouldProxyMiss } from "./helpers.js";
|
|
4
|
+
import { RequestBodyTooLargeError, buildContentWithToolCallsChunks, buildContentWithToolCallsCompletion, buildTextChunks, buildTextCompletion, buildToolCallChunks, buildToolCallCompletion, buildUsageChunk, describeMatch, extractOverrides, flattenHeaders, getContext, getTestId, isAudioResponse, isContentWithToolCallsResponse, isEmbeddingResponse, isErrorResponse, isImageResponse, isJSONResponse, isJsonObject, isResponseFactory, isTextResponse, isToolCallResponse, isTranscriptionResponse, isVideoResponse, markMintedRequestId, readBody, readBodyBufferBounded, resolveFixtureBlockOutcome, resolveReasoningForModel, resolveRequestId, resolveResponse, resolveStrictMode, resolveTestId, resolveUsage, serializeErrorResponse, strictNoMatchLogLine, strictNoMatchMessage, strictOverrideField, validateToolsField, wouldProxyMiss } from "./helpers.js";
|
|
4
5
|
import { normalizeLiveOptions } from "./live-fixture.js";
|
|
5
6
|
import { writeErrorResponse, writeSSEStream } from "./sse-writer.js";
|
|
6
7
|
import { CHAOS_FIELDS, CHAOS_FIELD_NAMES, applyChaosAction, awaitChaosLatency, describeUnwritableReason, evaluateChaos, isChaosScope, parseChaosField, resetChaosWarnings, resolveChaosConfig, responseGoneReason } from "./chaos.js";
|
|
7
|
-
import { INJECTED_STATUS_RANGE, claimOneShotError, clearFixtureQueue, entryToFixture, isInjectableStatus, isOneShotError, queueOneShotError, releaseOneShotError, validateFixtures } from "./fixture-loader.js";
|
|
8
|
+
import { FixtureLoadError, INJECTED_STATUS_RANGE, claimOneShotError, clearFixtureQueue, entryToFixture, isInjectableStatus, isOneShotError, queueOneShotError, releaseOneShotError, validateFixtures } from "./fixture-loader.js";
|
|
8
9
|
import { matchFixtureDiagnostic, recordMatchOptions } from "./router.js";
|
|
9
10
|
import { proxyAndRecord } from "./recorder.js";
|
|
10
11
|
import { Logger } from "./logger.js";
|
|
@@ -37,6 +38,7 @@ import { handleSearch } from "./search.js";
|
|
|
37
38
|
import { handleRerank } from "./rerank.js";
|
|
38
39
|
import { handleModeration } from "./moderation.js";
|
|
39
40
|
import { clearFineTuningStore, handleFineTuningCancel, handleFineTuningCreate, handleFineTuningEvents, handleFineTuningList, handleFineTuningRetrieve } from "./fine-tuning.js";
|
|
41
|
+
import { clearVectorStoreStore, handleVectorFileBatchesCancel, handleVectorFileBatchesCreate, handleVectorFileBatchesFiles, handleVectorFileBatchesRetrieve, handleVectorStoreFilesCreate, handleVectorStoreFilesDelete, handleVectorStoreFilesList, handleVectorStoreFilesRetrieve, handleVectorStoresCreate, handleVectorStoresDelete, handleVectorStoresList, handleVectorStoresModify, handleVectorStoresRetrieve, handleVectorStoresSearch } from "./vector-stores.js";
|
|
40
42
|
import { FILES_BODY_DRAIN_MAX_BYTES, FILES_BODY_MAX_BYTES, FILES_BODY_OVERSIZED, clearFileStore, handleFilesContent, handleFilesCreate, handleFilesDelete, handleFilesList, handleFilesRetrieve } from "./files.js";
|
|
41
43
|
import { clearBatchStore, handleBatchesCancel, handleBatchesCreate, handleBatchesList, handleBatchesRetrieve } from "./batches.js";
|
|
42
44
|
import { upgradeToWebSocket } from "./ws-framing.js";
|
|
@@ -44,41 +46,17 @@ import { handleLiveSession } from "./ws-live.js";
|
|
|
44
46
|
import { handleWebSocketResponses } from "./ws-responses.js";
|
|
45
47
|
import { handleWebSocketRealtime } from "./ws-realtime.js";
|
|
46
48
|
import { handleWebSocketGeminiLive } from "./ws-gemini-live.js";
|
|
47
|
-
import { BYTEPLUS_VIDEO_STATUS_RE, BYTEPLUS_VIDEO_SUBMIT_RE,
|
|
49
|
+
import { AZURE_DEPLOYMENT_RE, BATCHES_CANCEL_RE, BATCHES_ID_RE, BATCHES_PATH, BEDROCK_CONVERSE_RE, BEDROCK_CONVERSE_STREAM_RE, BEDROCK_INVOKE_RE, BEDROCK_STREAM_RE, BYTEPLUS_VIDEO_STATUS_RE, BYTEPLUS_VIDEO_SUBMIT_RE, COHERE_CHAT_PATH, COHERE_EMBED_PATH, COMPLETIONS_PATH, CONTROL_PREFIX, ELEVENLABS_MUSIC_RE, ELEVENLABS_SOUND_GENERATION_PATH, ELEVENLABS_TTS_RE, ELEVENLABS_VOICE_CREATE_PATH, ELEVENLABS_VOICE_DESIGN_PATH, ELEVENLABS_VOICE_RE, EMBEDDINGS_PATH, FAL_QUEUE_REQUESTS_RE, FAL_QUEUE_SUBMIT_RE, FAL_ROUTE_RE, FAL_RUN_RE, FILES_CONTENT_RE, FILES_ID_RE, FILES_PATH, FINE_TUNING_CANCEL_RE, FINE_TUNING_EVENTS_RE, FINE_TUNING_ID_RE, FINE_TUNING_JOBS_PATH, GEMINI_EMBED_RE, GEMINI_INTERACTIONS_PATH, GEMINI_LIVE_PATH, GEMINI_PATH_RE, GEMINI_PREDICT_RE, GROK_VIDEO_STATUS_RE, GROK_VIDEO_SUBMIT_PATH, HEALTH_PATH, IMAGES_EDIT_PATH, IMAGES_PATH, IMAGES_VARIATIONS_PATH, LIVE_PATH, MESSAGES_PATH, METRICS_PATH, MODELS_PATH, MODERATIONS_PATH, OLLAMA_CHAT_PATH, OLLAMA_EMBEDDINGS_PATH, OLLAMA_EMBED_PATH, OLLAMA_GENERATE_PATH, OLLAMA_TAGS_PATH, OPENROUTER_CREDITS_PATH, OPENROUTER_KEY_PATH, OPENROUTER_MODELS_PATH, OPENROUTER_VIDEOS_PATH, OPENROUTER_VIDEO_CONTENT_RE, OPENROUTER_VIDEO_MODELS_PATH, OPENROUTER_VIDEO_STATUS_RE, READY_PATH, REALTIME_PATH, REQUESTS_PATH, RERANK_PATH, RESPONSES_PATH, SEARCH_PATH, SPEECH_PATH, TRANSCRIPTIONS_PATH, TRANSLATIONS_PATH, VECTOR_STORES_BATCH_CANCEL_RE, VECTOR_STORES_BATCH_FILES_RE, VECTOR_STORES_BATCH_RE, VECTOR_STORES_FILES_RE, VECTOR_STORES_FILE_BATCHES_RE, VECTOR_STORES_FILE_RE, VECTOR_STORES_ID_RE, VECTOR_STORES_PATH, VECTOR_STORES_SEARCH_RE, VEO_OPERATION_RE, VEO_PREDICT_LRO_RE, VERTEX_AI_RE, VIDEOS_PATH, matchRouteDefinition } from "./route-registry.js";
|
|
50
|
+
import { CATALOG_ROUTES, buildOpenApiDocument } from "./openapi.js";
|
|
51
|
+
import { DESTROYED_STATUS_LABEL, createMetricsRegistry, normalizePathLabel } from "./metrics.js";
|
|
52
|
+
import { MCP_FAKES_DEFAULT_MOUNT, McpFakeStore, McpFakesAddError, blockIdOf } from "./mcp-fakes.js";
|
|
53
|
+
import { MCPMock } from "./mcp-mock.js";
|
|
54
|
+
import { ensureFakeMount, findFakeMount, handOffMcpFakes, isFakeMount, mountConflictDetail } from "./mcp-fakes-mount.js";
|
|
48
55
|
import * as http$1 from "node:http";
|
|
49
56
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
50
57
|
|
|
51
58
|
//#region src/server.ts
|
|
52
59
|
const liveClosers = /* @__PURE__ */ new WeakMap();
|
|
53
|
-
const COMPLETIONS_PATH = "/v1/chat/completions";
|
|
54
|
-
const RESPONSES_PATH = "/v1/responses";
|
|
55
|
-
const LIVE_PATH = "/v1/live/sessions";
|
|
56
|
-
const REALTIME_PATH = "/v1/realtime";
|
|
57
|
-
const GEMINI_LIVE_PATH = "/ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent";
|
|
58
|
-
const MESSAGES_PATH = "/v1/messages";
|
|
59
|
-
const EMBEDDINGS_PATH = "/v1/embeddings";
|
|
60
|
-
const COHERE_CHAT_PATH = "/v2/chat";
|
|
61
|
-
const COHERE_EMBED_PATH = "/v2/embed";
|
|
62
|
-
const SEARCH_PATH = "/search";
|
|
63
|
-
const RERANK_PATH = "/v2/rerank";
|
|
64
|
-
const MODERATIONS_PATH = "/v1/moderations";
|
|
65
|
-
const IMAGES_PATH = "/v1/images/generations";
|
|
66
|
-
const IMAGES_EDIT_PATH = "/v1/images/edits";
|
|
67
|
-
const IMAGES_VARIATIONS_PATH = "/v1/images/variations";
|
|
68
|
-
const SPEECH_PATH = "/v1/audio/speech";
|
|
69
|
-
const TRANSCRIPTIONS_PATH = "/v1/audio/transcriptions";
|
|
70
|
-
const TRANSLATIONS_PATH = "/v1/audio/translations";
|
|
71
|
-
const VIDEOS_PATH = "/v1/videos";
|
|
72
|
-
const GEMINI_PREDICT_RE = /^\/v1beta\/models\/([^:]+):predict$/;
|
|
73
|
-
const ELEVENLABS_SOUND_GENERATION_PATH = "/v1/sound-generation";
|
|
74
|
-
const ELEVENLABS_TTS_RE = /^\/v1\/text-to-speech\/([^/]+)$/;
|
|
75
|
-
const ELEVENLABS_MUSIC_RE = /^\/v1\/music(?:\/(.+))?$/;
|
|
76
|
-
const ELEVENLABS_VOICE_DESIGN_PATH = "/v1/text-to-voice/design";
|
|
77
|
-
const ELEVENLABS_VOICE_CREATE_PATH = "/v1/text-to-voice";
|
|
78
|
-
const ELEVENLABS_VOICE_RE = /^\/v1\/voices\/([^/]+)$/;
|
|
79
|
-
const FAL_QUEUE_SUBMIT_RE = /^\/fal\/queue\/submit\/(.+)$/;
|
|
80
|
-
const FAL_QUEUE_REQUESTS_RE = /^\/fal\/queue\/requests\/(.+)$/;
|
|
81
|
-
const FAL_RUN_RE = /^\/fal\/run\/(.+)$/;
|
|
82
60
|
const DEFAULT_CHUNK_SIZE = 20;
|
|
83
61
|
const COMPAT_SUFFIXES = [
|
|
84
62
|
"/chat/completions",
|
|
@@ -107,34 +85,6 @@ function normalizeCompatPath(pathname, logger) {
|
|
|
107
85
|
}
|
|
108
86
|
return pathname;
|
|
109
87
|
}
|
|
110
|
-
const GEMINI_INTERACTIONS_PATH = "/v1beta/interactions";
|
|
111
|
-
const GEMINI_PATH_RE = /^\/v1beta\/models\/([^:]+):(generateContent|streamGenerateContent)$/;
|
|
112
|
-
const GEMINI_EMBED_RE = /^\/v1beta\/models\/([^:]+):embedContent$/;
|
|
113
|
-
const AZURE_DEPLOYMENT_RE = /^\/openai\/deployments\/([^/]+)\/(chat\/completions|embeddings)$/;
|
|
114
|
-
const BEDROCK_INVOKE_RE = /^\/model\/([^/]+)\/invoke$/;
|
|
115
|
-
const BEDROCK_STREAM_RE = /^\/model\/([^/]+)\/invoke-with-response-stream$/;
|
|
116
|
-
const BEDROCK_CONVERSE_RE = /^\/model\/([^/]+)\/converse$/;
|
|
117
|
-
const BEDROCK_CONVERSE_STREAM_RE = /^\/model\/([^/]+)\/converse-stream$/;
|
|
118
|
-
const VERTEX_AI_RE = /^\/v1\/projects\/[^/]+\/locations\/[^/]+\/publishers\/google\/models\/([^/:]+):(generateContent|streamGenerateContent)$/;
|
|
119
|
-
const OLLAMA_CHAT_PATH = "/api/chat";
|
|
120
|
-
const OLLAMA_GENERATE_PATH = "/api/generate";
|
|
121
|
-
const OLLAMA_EMBEDDINGS_PATH = "/api/embeddings";
|
|
122
|
-
const OLLAMA_EMBED_PATH = "/api/embed";
|
|
123
|
-
const OLLAMA_TAGS_PATH = "/api/tags";
|
|
124
|
-
const OPENROUTER_VIDEOS_PATH = "/api/v1/videos";
|
|
125
|
-
const OPENROUTER_VIDEO_MODELS_PATH = "/api/v1/videos/models";
|
|
126
|
-
const HEALTH_PATH = "/health";
|
|
127
|
-
const READY_PATH = "/ready";
|
|
128
|
-
const MODELS_PATH = "/v1/models";
|
|
129
|
-
const REQUESTS_PATH = "/v1/_requests";
|
|
130
|
-
const BATCHES_PATH = "/v1/batches";
|
|
131
|
-
const BATCHES_ID_RE = /^\/v1\/batches\/([^/]+)$/;
|
|
132
|
-
const BATCHES_CANCEL_RE = /^\/v1\/batches\/([^/]+)\/cancel$/;
|
|
133
|
-
const FILES_PATH = "/v1/files";
|
|
134
|
-
const FINE_TUNING_JOBS_PATH = "/v1/fine_tuning/jobs";
|
|
135
|
-
const FINE_TUNING_ID_RE = /^\/v1\/fine_tuning\/jobs\/([^/]+)$/;
|
|
136
|
-
const FINE_TUNING_CANCEL_RE = /^\/v1\/fine_tuning\/jobs\/([^/]+)\/cancel$/;
|
|
137
|
-
const FINE_TUNING_EVENTS_RE = /^\/v1\/fine_tuning\/jobs\/([^/]+)\/events$/;
|
|
138
88
|
const DEFAULT_MODELS = [
|
|
139
89
|
"gpt-4",
|
|
140
90
|
"gpt-4o",
|
|
@@ -144,7 +94,7 @@ const DEFAULT_MODELS = [
|
|
|
144
94
|
];
|
|
145
95
|
const CORS_HEADERS = {
|
|
146
96
|
"Access-Control-Allow-Origin": "*",
|
|
147
|
-
"Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
|
|
97
|
+
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
|
148
98
|
"Access-Control-Allow-Headers": "*",
|
|
149
99
|
"Access-Control-Expose-Headers": "X-Total-Count, X-Request-Id"
|
|
150
100
|
};
|
|
@@ -163,7 +113,6 @@ function handleNotFound(res, message) {
|
|
|
163
113
|
type: "not_found"
|
|
164
114
|
} }));
|
|
165
115
|
}
|
|
166
|
-
const CONTROL_PREFIX = "/__aimock";
|
|
167
116
|
/** The complete `GET /__aimock/fixtures` query-param vocabulary; anything else 400s. */
|
|
168
117
|
const FIXTURES_PARAMS = new Set(["include"]);
|
|
169
118
|
/** The complete `GET /__aimock/journal` query-param vocabulary; anything else 400s. */
|
|
@@ -202,6 +151,7 @@ function performFullReset(fixtures, targets) {
|
|
|
202
151
|
clearElevenLabsVoices();
|
|
203
152
|
clearFileStore();
|
|
204
153
|
clearFineTuningStore();
|
|
154
|
+
clearVectorStoreStore();
|
|
205
155
|
resetInteractionCounter();
|
|
206
156
|
resetEventIdCounter();
|
|
207
157
|
if (!targets) return;
|
|
@@ -214,6 +164,7 @@ function performFullReset(fixtures, targets) {
|
|
|
214
164
|
targets.veoVideoJobs.clear();
|
|
215
165
|
targets.grokVideoJobs.clear();
|
|
216
166
|
targets.bytePlusVideoJobs.clear();
|
|
167
|
+
for (const { handler } of targets.mounts ?? []) handler.clearMcpFakes?.();
|
|
217
168
|
if (targets.defaults.registry) targets.defaults.registry.setGauge("aimock_fixtures_loaded", {}, fixtures.length);
|
|
218
169
|
}
|
|
219
170
|
/**
|
|
@@ -268,9 +219,14 @@ function fixtureResponseKind(response) {
|
|
|
268
219
|
* scoping path does not also do. (Splitting on "," additionally broke every
|
|
269
220
|
* legitimately comma-bearing id.) A raw `path.includes("testId=t1")` both
|
|
270
221
|
* prefix-collides with `t10` and matches unrelated params like `notTestId`.
|
|
222
|
+
*
|
|
223
|
+
* An MCP mount records the test id it resolved on the entry (I9, B3): a
|
|
224
|
+
* decoded header or query value, or the one bound to the session at
|
|
225
|
+
* `initialize`. That value wins; LLM entries carry none and are attributed as
|
|
226
|
+
* before.
|
|
271
227
|
*/
|
|
272
228
|
function journalEntryTestId(entry) {
|
|
273
|
-
return resolveTestId(entry.headers, entry.path);
|
|
229
|
+
return entry.testId ?? resolveTestId(entry.headers, entry.path);
|
|
274
230
|
}
|
|
275
231
|
/**
|
|
276
232
|
* The testId a chaos override is stored under, and read back with — resolved by
|
|
@@ -309,19 +265,186 @@ function effectiveChaos(defaults, scopeId) {
|
|
|
309
265
|
if (!current) return {};
|
|
310
266
|
return isChaosScope(current) ? current.base ?? {} : current;
|
|
311
267
|
}
|
|
268
|
+
/** The `GET /__aimock/mcp/fakes` query-param vocabulary; anything else 400s. */
|
|
269
|
+
const MCP_FAKES_PARAMS = new Set([
|
|
270
|
+
"testId",
|
|
271
|
+
"context",
|
|
272
|
+
"mount"
|
|
273
|
+
]);
|
|
274
|
+
/** A block's `mount` path, read without running getters; the add validates it. */
|
|
275
|
+
function controlFakeMountOf(raw) {
|
|
276
|
+
if (typeof raw !== "object" || raw === null) return MCP_FAKES_DEFAULT_MOUNT;
|
|
277
|
+
const value = Object.getOwnPropertyDescriptor(raw, "mount")?.value;
|
|
278
|
+
return typeof value === "string" ? value : MCP_FAKES_DEFAULT_MOUNT;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Check one `POST /__aimock/fixtures` `mcpFakes` value against the live
|
|
282
|
+
* mounts (F10, W6) without changing anything. Returns every error in input
|
|
283
|
+
* order (an empty list when the value can be added), and `apply`, which adds
|
|
284
|
+
* the blocks (auto-mounting an MCPMock where no mount serves a block's path)
|
|
285
|
+
* and returns the number of blocks added.
|
|
286
|
+
*
|
|
287
|
+
* The whole value is checked before any mount is touched, and the check
|
|
288
|
+
* never adds to a real mount: each group is added to a throwaway store,
|
|
289
|
+
* whose errors are the ones reported (it numbers the add `control-api#1`, as
|
|
290
|
+
* a new mount would; for an existing MCPMock the rejected group is re-run on
|
|
291
|
+
* its atomic add, so the errors name that mount's next `<n>`, I7). A group
|
|
292
|
+
* whose path no request to it can reach (a mount that is not an MCPMock
|
|
293
|
+
* serves it, an MCPMock answers it as its root, or it is under the control
|
|
294
|
+
* prefix) is a mount conflict (one error per block).
|
|
295
|
+
*/
|
|
296
|
+
function planControlApiFakes(raw, mounts) {
|
|
297
|
+
const items = Array.isArray(raw) ? raw.map((block, position) => ({
|
|
298
|
+
position,
|
|
299
|
+
source: {
|
|
300
|
+
source: "control-api",
|
|
301
|
+
blockIndex: position,
|
|
302
|
+
raw: block
|
|
303
|
+
}
|
|
304
|
+
})) : [{
|
|
305
|
+
position: 0,
|
|
306
|
+
source: {
|
|
307
|
+
source: "control-api",
|
|
308
|
+
blockIndex: null,
|
|
309
|
+
raw
|
|
310
|
+
}
|
|
311
|
+
}];
|
|
312
|
+
const groups = /* @__PURE__ */ new Map();
|
|
313
|
+
if (items.length === 0) groups.set(MCP_FAKES_DEFAULT_MOUNT, {
|
|
314
|
+
path: MCP_FAKES_DEFAULT_MOUNT,
|
|
315
|
+
positions: [],
|
|
316
|
+
sources: []
|
|
317
|
+
});
|
|
318
|
+
for (const { position, source } of items) {
|
|
319
|
+
const path = controlFakeMountOf(source.raw);
|
|
320
|
+
const group = groups.get(path) ?? {
|
|
321
|
+
path,
|
|
322
|
+
positions: [],
|
|
323
|
+
sources: []
|
|
324
|
+
};
|
|
325
|
+
group.positions.push(position);
|
|
326
|
+
group.sources.push(source);
|
|
327
|
+
groups.set(path, group);
|
|
328
|
+
}
|
|
329
|
+
const found = [];
|
|
330
|
+
/** The paths `apply` auto-mounts, in order. */
|
|
331
|
+
const pending = [];
|
|
332
|
+
for (const group of groups.values()) {
|
|
333
|
+
const target = findFakeMount(mounts, group.path, pending);
|
|
334
|
+
if (target.kind === "conflict") {
|
|
335
|
+
group.positions.forEach((position) => {
|
|
336
|
+
const where = Array.isArray(raw) ? msg`mcpFakes[${position}]` : msg`mcpFakes`;
|
|
337
|
+
found.push({
|
|
338
|
+
position,
|
|
339
|
+
error: new FixtureLoadError({
|
|
340
|
+
rule: "mcp-fakes/mount-conflict",
|
|
341
|
+
file: null,
|
|
342
|
+
blockId: null,
|
|
343
|
+
entryId: null,
|
|
344
|
+
detail: msg`${where}: ${mountConflictDetail(group.path, target)}`
|
|
345
|
+
})
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
let failure = addFailure(() => new McpFakeStore().add(group.sources, { kind: "control-api" }));
|
|
351
|
+
if (target.kind === "none") pending.push(group.path);
|
|
352
|
+
if (!failure) continue;
|
|
353
|
+
if (target.kind === "mcp" && target.handler.addMcpFakes === MCPMock.prototype.addMcpFakes) {
|
|
354
|
+
const handler = target.handler;
|
|
355
|
+
failure = addFailure(() => handler.addMcpFakes?.(group.sources, { kind: "control-api" })) ?? failure;
|
|
356
|
+
}
|
|
357
|
+
for (const error of failure.errors) found.push({
|
|
358
|
+
position: positionOf(error, group),
|
|
359
|
+
error
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
found.sort((a, b) => a.position - b.position);
|
|
363
|
+
return {
|
|
364
|
+
errors: found.map((f) => f.error),
|
|
365
|
+
apply(wiring) {
|
|
366
|
+
let added = 0;
|
|
367
|
+
for (const group of groups.values()) {
|
|
368
|
+
const target = ensureFakeMount(mounts, group.path, {
|
|
369
|
+
journal: wiring.journal,
|
|
370
|
+
registry: wiring.defaults.registry,
|
|
371
|
+
logger: wiring.defaults.logger
|
|
372
|
+
}, wiring.defaults.logger);
|
|
373
|
+
if ("conflict" in target) continue;
|
|
374
|
+
const result = target.handler.addMcpFakes?.(group.sources, { kind: "control-api" });
|
|
375
|
+
for (const warning of result?.warnings ?? []) wiring.defaults.logger.warn(warning.message);
|
|
376
|
+
added += group.sources.length;
|
|
377
|
+
}
|
|
378
|
+
return added;
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
/** The `McpFakesAddError` that `add` throws, or `null` when it adds. */
|
|
383
|
+
function addFailure(add) {
|
|
384
|
+
try {
|
|
385
|
+
add();
|
|
386
|
+
return null;
|
|
387
|
+
} catch (err) {
|
|
388
|
+
if (err instanceof McpFakesAddError) return err;
|
|
389
|
+
throw err;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* The request position of the block an add error names. The add numbers a
|
|
394
|
+
* run-time group's blocks by their place in the group (`[<j>]`), or gives no
|
|
395
|
+
* index to a single-object value; an error tied to no block sorts with the
|
|
396
|
+
* group's first block.
|
|
397
|
+
*/
|
|
398
|
+
function positionOf(error, group) {
|
|
399
|
+
const first = group.positions[0] ?? 0;
|
|
400
|
+
if (error.blockId == null || error.file === null) return first;
|
|
401
|
+
const at = group.positions.findIndex((_, j) => error.blockId === blockIdOf(error.file ?? "", "", j));
|
|
402
|
+
return at === -1 ? first : group.positions[at];
|
|
403
|
+
}
|
|
404
|
+
function hasFakesSnapshot(handler) {
|
|
405
|
+
return "fakesSnapshot" in handler && typeof handler.fakesSnapshot === "function";
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* `GET /__aimock/mcp/fakes` (spec 6.5, I9): for each MCP mount (or the one
|
|
409
|
+
* named by `mount`), the blocks that apply to the requested test id and
|
|
410
|
+
* context, with every entry id and its consumed state for that test id.
|
|
411
|
+
* A mount is listed when it takes fakes (it implements `addMcpFakes`, the
|
|
412
|
+
* check every other fakes path uses); one with no `fakesSnapshot` lists no
|
|
413
|
+
* blocks.
|
|
414
|
+
*/
|
|
415
|
+
function listControlApiFakes(mounts, testId, context, mount) {
|
|
416
|
+
const listed = mounts.flatMap(({ path, handler }) => isFakeMount(handler) && (mount === null || path === mount) ? [{
|
|
417
|
+
mount: path,
|
|
418
|
+
blocks: hasFakesSnapshot(handler) ? handler.fakesSnapshot(testId, context) : []
|
|
419
|
+
}] : []);
|
|
420
|
+
return mount !== null && listed.length === 0 ? null : listed;
|
|
421
|
+
}
|
|
312
422
|
/**
|
|
313
423
|
* Handle requests under `/__aimock/`. Returns `true` if the request was
|
|
314
424
|
* handled, `false` if the path doesn't match the control prefix.
|
|
315
425
|
*/
|
|
316
|
-
async function handleControlAPI(req, res, pathname, searchParams, fixtures, journal, videoStates, openRouterVideoJobs, veoVideoJobs, grokVideoJobs, bytePlusVideoJobs, defaults) {
|
|
426
|
+
async function handleControlAPI(req, res, pathname, searchParams, fixtures, journal, videoStates, openRouterVideoJobs, veoVideoJobs, grokVideoJobs, bytePlusVideoJobs, defaults, mounts) {
|
|
317
427
|
if (!pathname.startsWith(CONTROL_PREFIX)) return false;
|
|
318
|
-
const subPath = pathname.slice(
|
|
428
|
+
const subPath = pathname.slice(CONTROL_PREFIX.length);
|
|
319
429
|
setCorsHeaders(res);
|
|
320
430
|
if (subPath === "/health" && req.method === "GET") {
|
|
321
431
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
322
432
|
res.end(JSON.stringify({ status: "ok" }));
|
|
323
433
|
return true;
|
|
324
434
|
}
|
|
435
|
+
if (subPath === "/openapi.json" && req.method === "GET") {
|
|
436
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
437
|
+
res.end(JSON.stringify(buildOpenApiDocument()));
|
|
438
|
+
return true;
|
|
439
|
+
}
|
|
440
|
+
if (subPath === "/routes" && req.method === "GET") {
|
|
441
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
442
|
+
res.end(JSON.stringify({
|
|
443
|
+
count: CATALOG_ROUTES.length,
|
|
444
|
+
routes: CATALOG_ROUTES
|
|
445
|
+
}));
|
|
446
|
+
return true;
|
|
447
|
+
}
|
|
325
448
|
if (subPath === "/journal" && req.method === "GET") {
|
|
326
449
|
const bad = (message) => {
|
|
327
450
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
@@ -472,6 +595,24 @@ async function handleControlAPI(req, res, pathname, searchParams, fixtures, jour
|
|
|
472
595
|
res.end(JSON.stringify({ chaos: effectiveChaos(defaults, scopeId) }));
|
|
473
596
|
return true;
|
|
474
597
|
}
|
|
598
|
+
if (subPath === "/mcp/fakes" && req.method === "GET") {
|
|
599
|
+
const reply = (status, body) => {
|
|
600
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
601
|
+
res.end(JSON.stringify(body));
|
|
602
|
+
return true;
|
|
603
|
+
};
|
|
604
|
+
for (const key of searchParams.keys()) if (!MCP_FAKES_PARAMS.has(key)) return reply(400, { error: `Unknown query parameter: '${key}'. Supported: ${[...MCP_FAKES_PARAMS].join(", ")}` });
|
|
605
|
+
const testId = searchParams.get("testId") || null;
|
|
606
|
+
const context = searchParams.get("context") || null;
|
|
607
|
+
const mount = searchParams.get("mount") || null;
|
|
608
|
+
const listed = listControlApiFakes(mounts, testId, context, mount);
|
|
609
|
+
if (listed === null) return reply(404, { error: build(msg`No MCP mount at ${quote(mount ?? "")}`) });
|
|
610
|
+
return reply(200, {
|
|
611
|
+
testId,
|
|
612
|
+
context,
|
|
613
|
+
mounts: listed
|
|
614
|
+
});
|
|
615
|
+
}
|
|
475
616
|
if (subPath === "/fixtures" && req.method === "POST") {
|
|
476
617
|
let raw;
|
|
477
618
|
try {
|
|
@@ -493,29 +634,60 @@ async function handleControlAPI(req, res, pathname, searchParams, fixtures, jour
|
|
|
493
634
|
res.end(JSON.stringify({ error: `Invalid JSON: ${msg}` }));
|
|
494
635
|
return true;
|
|
495
636
|
}
|
|
496
|
-
|
|
637
|
+
const withFakes = typeof parsed === "object" && parsed !== null && Object.hasOwn(parsed, "mcpFakes");
|
|
638
|
+
if (parsed === null || !Array.isArray(parsed.fixtures) && !(withFakes && parsed.fixtures === void 0)) {
|
|
497
639
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
498
640
|
res.end(JSON.stringify({ error: "Missing or invalid \"fixtures\" array" }));
|
|
499
641
|
return true;
|
|
500
642
|
}
|
|
501
|
-
const
|
|
643
|
+
const entries = Array.isArray(parsed.fixtures) ? parsed.fixtures : [];
|
|
644
|
+
const missingMatchIndex = entries.findIndex((entry) => entry !== null && typeof entry === "object" && !Array.isArray(entry) && entry.match == null);
|
|
645
|
+
if (missingMatchIndex !== -1) {
|
|
646
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
647
|
+
res.end(JSON.stringify({ error: `Fixture at index ${missingMatchIndex} is missing match` }));
|
|
648
|
+
return true;
|
|
649
|
+
}
|
|
650
|
+
const invalidMatchIndex = entries.findIndex((entry) => entry !== null && typeof entry === "object" && !Array.isArray(entry) && entry.match != null && (typeof entry.match !== "object" || Array.isArray(entry.match)));
|
|
651
|
+
if (invalidMatchIndex !== -1) {
|
|
652
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
653
|
+
res.end(JSON.stringify({ error: `Fixture at index ${invalidMatchIndex}: match must be an object` }));
|
|
654
|
+
return true;
|
|
655
|
+
}
|
|
656
|
+
const converted = entries.map((e) => entryToFixture(e));
|
|
502
657
|
const errors = validateFixtures(converted).filter((i) => i.severity === "error");
|
|
503
|
-
|
|
658
|
+
const fakes = withFakes ? planControlApiFakes(parsed.mcpFakes, mounts) : null;
|
|
659
|
+
const fakeErrors = fakes?.errors ?? [];
|
|
660
|
+
const validationFailed = (details) => {
|
|
504
661
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
505
662
|
res.end(JSON.stringify({
|
|
506
663
|
error: "Validation failed",
|
|
507
|
-
details
|
|
664
|
+
details
|
|
508
665
|
}));
|
|
509
666
|
return true;
|
|
667
|
+
};
|
|
668
|
+
if (errors.length > 0 || fakeErrors.length > 0) return validationFailed([...errors, ...fakeErrors.map((e) => e.toJSON())]);
|
|
669
|
+
let mcpFakesAdded = 0;
|
|
670
|
+
if (fakes) try {
|
|
671
|
+
mcpFakesAdded = fakes.apply({
|
|
672
|
+
journal,
|
|
673
|
+
defaults
|
|
674
|
+
});
|
|
675
|
+
} catch (err) {
|
|
676
|
+
if (!(err instanceof McpFakesAddError)) throw err;
|
|
677
|
+
return validationFailed(err.errors.map((e) => e.toJSON()));
|
|
510
678
|
}
|
|
511
679
|
fixtures.push(...converted);
|
|
512
680
|
if (defaults.registry) defaults.registry.setGauge("aimock_fixtures_loaded", {}, fixtures.length);
|
|
513
681
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
514
|
-
res.end(JSON.stringify(
|
|
682
|
+
res.end(JSON.stringify(withFakes ? {
|
|
683
|
+
added: converted.length,
|
|
684
|
+
mcpFakesAdded
|
|
685
|
+
} : { added: converted.length }));
|
|
515
686
|
return true;
|
|
516
687
|
}
|
|
517
688
|
if (subPath === "/fixtures" && req.method === "DELETE") {
|
|
518
689
|
clearFixtureQueue(fixtures);
|
|
690
|
+
for (const { handler } of mounts) handler.clearMcpFakes?.();
|
|
519
691
|
if (defaults.registry) defaults.registry.setGauge("aimock_fixtures_loaded", {}, fixtures.length);
|
|
520
692
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
521
693
|
res.end(JSON.stringify({ cleared: true }));
|
|
@@ -528,7 +700,8 @@ async function handleControlAPI(req, res, pathname, searchParams, fixtures, jour
|
|
|
528
700
|
veoVideoJobs,
|
|
529
701
|
grokVideoJobs,
|
|
530
702
|
bytePlusVideoJobs,
|
|
531
|
-
defaults
|
|
703
|
+
defaults,
|
|
704
|
+
mounts
|
|
532
705
|
});
|
|
533
706
|
if (subPath === "/reset" && req.method === "POST") {
|
|
534
707
|
performFullReset(fixtures, resetTargets());
|
|
@@ -737,6 +910,51 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
|
|
|
737
910
|
} }));
|
|
738
911
|
return;
|
|
739
912
|
}
|
|
913
|
+
const nullMessageIndex = body.messages.findIndex((message) => message === null);
|
|
914
|
+
if (nullMessageIndex !== -1) {
|
|
915
|
+
const message = `Invalid request: messages[${nullMessageIndex}] must be an object`;
|
|
916
|
+
journal.add({
|
|
917
|
+
method: req.method ?? "POST",
|
|
918
|
+
path: req.url ?? COMPLETIONS_PATH,
|
|
919
|
+
headers: flattenHeaders(req.headers),
|
|
920
|
+
body: null,
|
|
921
|
+
response: {
|
|
922
|
+
status: 400,
|
|
923
|
+
fixture: null
|
|
924
|
+
}
|
|
925
|
+
});
|
|
926
|
+
writeErrorResponse(res, 400, openRouter ? serializeOpenRouterError(400, message) : JSON.stringify({ error: {
|
|
927
|
+
message,
|
|
928
|
+
type: "invalid_request_error",
|
|
929
|
+
param: null,
|
|
930
|
+
code: null
|
|
931
|
+
} }));
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
if (new URL(req.url ?? COMPLETIONS_PATH, "http://localhost").pathname === COMPLETIONS_PATH) {
|
|
935
|
+
let toolsError = validateToolsField(body.tools);
|
|
936
|
+
if (!toolsError && Array.isArray(body.tools)) {
|
|
937
|
+
const nullFunctionIndex = body.tools.findIndex((tool) => tool?.function === null);
|
|
938
|
+
if (nullFunctionIndex !== -1) toolsError = `tools[${nullFunctionIndex}].function must not be null`;
|
|
939
|
+
}
|
|
940
|
+
if (toolsError) {
|
|
941
|
+
journal.add({
|
|
942
|
+
method: req.method ?? "POST",
|
|
943
|
+
path: req.url ?? COMPLETIONS_PATH,
|
|
944
|
+
headers: flattenHeaders(req.headers),
|
|
945
|
+
body: null,
|
|
946
|
+
response: {
|
|
947
|
+
status: 400,
|
|
948
|
+
fixture: null
|
|
949
|
+
}
|
|
950
|
+
});
|
|
951
|
+
writeErrorResponse(res, 400, JSON.stringify({ error: {
|
|
952
|
+
message: toolsError,
|
|
953
|
+
type: "invalid_request_error"
|
|
954
|
+
} }));
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
740
958
|
const method = req.method ?? "POST";
|
|
741
959
|
const path = req.url ?? COMPLETIONS_PATH;
|
|
742
960
|
const flatHeaders = flattenHeaders(req.headers);
|
|
@@ -1146,9 +1364,24 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
|
|
|
1146
1364
|
}
|
|
1147
1365
|
/** @internal Config startup uses this to avoid parsing a selected auth source twice. */
|
|
1148
1366
|
async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mounts, serviceFixtures) {
|
|
1367
|
+
const logger = new Logger(options?.logLevel ?? "silent");
|
|
1368
|
+
const mountList = mounts ?? [];
|
|
1369
|
+
const handOff = serviceFixtures?.mcpFakes && serviceFixtures.mcpFakes.length > 0 ? handOffMcpFakes(mountList, serviceFixtures.mcpFakes, logger) : null;
|
|
1370
|
+
try {
|
|
1371
|
+
return await startServer(fixtures, options, resolvedAuth, mountList, logger, serviceFixtures, () => handOff?.commit());
|
|
1372
|
+
} catch (err) {
|
|
1373
|
+
handOff?.undo();
|
|
1374
|
+
throw err;
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
/**
|
|
1378
|
+
* The server of `createServerWithResolvedAuth`, after the MCP fakes hand-off.
|
|
1379
|
+
* `commitHandOff` runs once the server listens; if it throws, the server is
|
|
1380
|
+
* closed and start rejects.
|
|
1381
|
+
*/
|
|
1382
|
+
async function startServer(fixtures, options, resolvedAuth, mountList, logger, serviceFixtures, commitHandOff) {
|
|
1149
1383
|
const host = options?.host ?? "127.0.0.1";
|
|
1150
1384
|
const port = options?.port ?? 0;
|
|
1151
|
-
const logger = new Logger(options?.logLevel ?? "silent");
|
|
1152
1385
|
const registry = options?.metrics ? createMetricsRegistry() : void 0;
|
|
1153
1386
|
const serverOptions = options ?? {};
|
|
1154
1387
|
const chaosByTestId = /* @__PURE__ */ new Map();
|
|
@@ -1437,9 +1670,10 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1437
1670
|
else res.destroy();
|
|
1438
1671
|
}
|
|
1439
1672
|
}
|
|
1440
|
-
|
|
1673
|
+
for (const { handler } of mountList) {
|
|
1441
1674
|
if (handler.setJournal) handler.setJournal(journal);
|
|
1442
1675
|
if (registry && handler.setRegistry) handler.setRegistry(registry);
|
|
1676
|
+
if (handler.setLogger) handler.setLogger(logger);
|
|
1443
1677
|
}
|
|
1444
1678
|
if (registry) registry.setGauge("aimock_fixtures_loaded", {}, fixtures.length);
|
|
1445
1679
|
const server = http$1.createServer((req, res) => {
|
|
@@ -1462,7 +1696,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1462
1696
|
if (recorded) return;
|
|
1463
1697
|
recorded = true;
|
|
1464
1698
|
try {
|
|
1465
|
-
const normalizedPath = normalizePathLabel(pathname,
|
|
1699
|
+
const normalizedPath = normalizePathLabel(pathname, mountList.map((m) => m.path));
|
|
1466
1700
|
const method = req.method ?? "UNKNOWN";
|
|
1467
1701
|
registry.incrementCounter("aimock_requests_total", {
|
|
1468
1702
|
method,
|
|
@@ -1483,7 +1717,36 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1483
1717
|
if (!res.writableFinished) recordOutcome(DESTROYED_STATUS_LABEL);
|
|
1484
1718
|
});
|
|
1485
1719
|
}
|
|
1486
|
-
|
|
1720
|
+
let parsedUrl;
|
|
1721
|
+
try {
|
|
1722
|
+
parsedUrl = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
1723
|
+
} catch (err) {
|
|
1724
|
+
const route = `${req.method ?? "?"} ${req.url ?? "?"}`;
|
|
1725
|
+
const msg = err instanceof Error ? err.message : "Internal error";
|
|
1726
|
+
logger.error(`${route}: ${msg}`);
|
|
1727
|
+
if (err instanceof Error && err.stack) logger.debug(err.stack);
|
|
1728
|
+
try {
|
|
1729
|
+
journal.add({
|
|
1730
|
+
method: req.method ?? "?",
|
|
1731
|
+
path: req.url ?? "?",
|
|
1732
|
+
headers: flattenHeaders(req.headers),
|
|
1733
|
+
body: null,
|
|
1734
|
+
response: {
|
|
1735
|
+
status: 400,
|
|
1736
|
+
fixture: null,
|
|
1737
|
+
source: "internal"
|
|
1738
|
+
}
|
|
1739
|
+
});
|
|
1740
|
+
} catch (jErr) {
|
|
1741
|
+
logger.warn(`${route}: journal write failed after handler error: ${jErr instanceof Error ? jErr.message : String(jErr)}`);
|
|
1742
|
+
}
|
|
1743
|
+
setCorsHeaders(res);
|
|
1744
|
+
writeErrorResponse(res, 400, JSON.stringify({ error: {
|
|
1745
|
+
message: "Invalid request target",
|
|
1746
|
+
type: "invalid_request_error"
|
|
1747
|
+
} }));
|
|
1748
|
+
return;
|
|
1749
|
+
}
|
|
1487
1750
|
pathname = parsedUrl.pathname;
|
|
1488
1751
|
const originalPathname = pathname;
|
|
1489
1752
|
const isOpenRouter = isOpenRouterPath(originalPathname);
|
|
@@ -1492,7 +1755,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1492
1755
|
handleOptions(res);
|
|
1493
1756
|
return;
|
|
1494
1757
|
}
|
|
1495
|
-
if (!(req.method === "GET" && (pathname === HEALTH_PATH || pathname === READY_PATH || pathname ===
|
|
1758
|
+
if (!(req.method === "GET" && (pathname === HEALTH_PATH || pathname === READY_PATH || pathname === METRICS_PATH))) {
|
|
1496
1759
|
if (!validateRequestApiKey(req, resolvedAuth.policy).ok) {
|
|
1497
1760
|
writeApiKeyHttpRejection(res, setCorsHeaders);
|
|
1498
1761
|
return;
|
|
@@ -1500,14 +1763,12 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1500
1763
|
markAuthenticatedRequest(req, resolvedAuth.policy);
|
|
1501
1764
|
}
|
|
1502
1765
|
if (pathname.startsWith(CONTROL_PREFIX)) {
|
|
1503
|
-
await handleControlAPI(req, res, pathname, parsedUrl.searchParams, fixtures, journal, videoStates, openRouterVideoJobs, veoVideoJobs, grokVideoJobs, bytePlusVideoJobs, defaults);
|
|
1766
|
+
await handleControlAPI(req, res, pathname, parsedUrl.searchParams, fixtures, journal, videoStates, openRouterVideoJobs, veoVideoJobs, grokVideoJobs, bytePlusVideoJobs, defaults, mountList);
|
|
1504
1767
|
return;
|
|
1505
1768
|
}
|
|
1506
|
-
if (
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
if (await handler.handleRequest(req, res, subPath)) return;
|
|
1510
|
-
}
|
|
1769
|
+
for (const { path: mountPath, handler } of mountList) if (pathname === mountPath || pathname.startsWith(mountPath + "/")) {
|
|
1770
|
+
const subPath = pathname.slice(mountPath.length) || "/";
|
|
1771
|
+
if (await handler.handleRequest(req, res, subPath)) return;
|
|
1511
1772
|
}
|
|
1512
1773
|
if (pathname === OLLAMA_CHAT_PATH && req.method === "POST") {
|
|
1513
1774
|
try {
|
|
@@ -1602,15 +1863,15 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1602
1863
|
}
|
|
1603
1864
|
return;
|
|
1604
1865
|
}
|
|
1605
|
-
if (pathname ===
|
|
1866
|
+
if (pathname === OPENROUTER_MODELS_PATH && req.method === "GET") {
|
|
1606
1867
|
handleOpenRouterModels(req, res, fixtures, journal, defaults, setCorsHeaders);
|
|
1607
1868
|
return;
|
|
1608
1869
|
}
|
|
1609
|
-
if (pathname ===
|
|
1870
|
+
if (pathname === OPENROUTER_KEY_PATH && req.method === "GET") {
|
|
1610
1871
|
handleOpenRouterKey(req, res, journal, setCorsHeaders);
|
|
1611
1872
|
return;
|
|
1612
1873
|
}
|
|
1613
|
-
if (pathname ===
|
|
1874
|
+
if (pathname === OPENROUTER_CREDITS_PATH && req.method === "GET") {
|
|
1614
1875
|
handleOpenRouterCredits(req, res, journal, setCorsHeaders);
|
|
1615
1876
|
return;
|
|
1616
1877
|
}
|
|
@@ -1623,12 +1884,12 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1623
1884
|
if (!azureDeploymentId) pathname = normalizeCompatPath(pathname, logger);
|
|
1624
1885
|
if (pathname === HEALTH_PATH && req.method === "GET") {
|
|
1625
1886
|
setCorsHeaders(res);
|
|
1626
|
-
if (
|
|
1887
|
+
if (mountList.length > 0) {
|
|
1627
1888
|
const services = { llm: {
|
|
1628
1889
|
status: "ok",
|
|
1629
1890
|
fixtures: fixtures.length
|
|
1630
1891
|
} };
|
|
1631
|
-
for (const { path: mountPath, handler } of
|
|
1892
|
+
for (const { path: mountPath, handler } of mountList) if (handler.health) {
|
|
1632
1893
|
const name = mountPath.replace(/^\//, "");
|
|
1633
1894
|
services[name] = handler.health();
|
|
1634
1895
|
}
|
|
@@ -1649,7 +1910,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1649
1910
|
res.end(JSON.stringify({ status: "ready" }));
|
|
1650
1911
|
return;
|
|
1651
1912
|
}
|
|
1652
|
-
if (pathname ===
|
|
1913
|
+
if (pathname === METRICS_PATH && req.method === "GET") {
|
|
1653
1914
|
if (!registry) {
|
|
1654
1915
|
handleNotFound(res, "Not found");
|
|
1655
1916
|
return;
|
|
@@ -1797,14 +2058,140 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1797
2058
|
}
|
|
1798
2059
|
return;
|
|
1799
2060
|
}
|
|
2061
|
+
const vsSearchMatch = pathname.match(VECTOR_STORES_SEARCH_RE);
|
|
2062
|
+
if (vsSearchMatch && req.method === "POST") {
|
|
2063
|
+
try {
|
|
2064
|
+
const raw = await readBody(req);
|
|
2065
|
+
await handleVectorStoresSearch(req, res, vsSearchMatch[1], raw, journal, defaults, setCorsHeaders);
|
|
2066
|
+
} catch (err) {
|
|
2067
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2068
|
+
}
|
|
2069
|
+
return;
|
|
2070
|
+
}
|
|
2071
|
+
const vsBatchFilesMatch = pathname.match(VECTOR_STORES_BATCH_FILES_RE);
|
|
2072
|
+
if (vsBatchFilesMatch && req.method === "GET") {
|
|
2073
|
+
try {
|
|
2074
|
+
await handleVectorFileBatchesFiles(req, res, vsBatchFilesMatch[1], vsBatchFilesMatch[2], journal, defaults, setCorsHeaders);
|
|
2075
|
+
} catch (err) {
|
|
2076
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2077
|
+
}
|
|
2078
|
+
return;
|
|
2079
|
+
}
|
|
2080
|
+
const vsBatchCancelMatch = pathname.match(VECTOR_STORES_BATCH_CANCEL_RE);
|
|
2081
|
+
if (vsBatchCancelMatch && req.method === "POST") {
|
|
2082
|
+
try {
|
|
2083
|
+
await readBody(req);
|
|
2084
|
+
await handleVectorFileBatchesCancel(req, res, vsBatchCancelMatch[1], vsBatchCancelMatch[2], journal, defaults, setCorsHeaders);
|
|
2085
|
+
} catch (err) {
|
|
2086
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2087
|
+
}
|
|
2088
|
+
return;
|
|
2089
|
+
}
|
|
2090
|
+
const vsBatchMatch = pathname.match(VECTOR_STORES_BATCH_RE);
|
|
2091
|
+
if (vsBatchMatch && req.method === "GET") {
|
|
2092
|
+
try {
|
|
2093
|
+
await handleVectorFileBatchesRetrieve(req, res, vsBatchMatch[1], vsBatchMatch[2], journal, defaults, setCorsHeaders);
|
|
2094
|
+
} catch (err) {
|
|
2095
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2096
|
+
}
|
|
2097
|
+
return;
|
|
2098
|
+
}
|
|
2099
|
+
const vsFileMatch = pathname.match(VECTOR_STORES_FILE_RE);
|
|
2100
|
+
if (vsFileMatch && req.method === "GET") {
|
|
2101
|
+
try {
|
|
2102
|
+
await handleVectorStoreFilesRetrieve(req, res, vsFileMatch[1], vsFileMatch[2], journal, defaults, setCorsHeaders);
|
|
2103
|
+
} catch (err) {
|
|
2104
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2105
|
+
}
|
|
2106
|
+
return;
|
|
2107
|
+
}
|
|
2108
|
+
if (vsFileMatch && req.method === "DELETE") {
|
|
2109
|
+
try {
|
|
2110
|
+
await handleVectorStoreFilesDelete(req, res, vsFileMatch[1], vsFileMatch[2], journal, defaults, setCorsHeaders);
|
|
2111
|
+
} catch (err) {
|
|
2112
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2113
|
+
}
|
|
2114
|
+
return;
|
|
2115
|
+
}
|
|
2116
|
+
const vsIdMatch = pathname.match(VECTOR_STORES_ID_RE);
|
|
2117
|
+
if (vsIdMatch && req.method === "GET") {
|
|
2118
|
+
try {
|
|
2119
|
+
await handleVectorStoresRetrieve(req, res, vsIdMatch[1], journal, defaults, setCorsHeaders);
|
|
2120
|
+
} catch (err) {
|
|
2121
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2122
|
+
}
|
|
2123
|
+
return;
|
|
2124
|
+
}
|
|
2125
|
+
if (vsIdMatch && req.method === "POST") {
|
|
2126
|
+
try {
|
|
2127
|
+
const raw = await readBody(req);
|
|
2128
|
+
await handleVectorStoresModify(req, res, vsIdMatch[1], raw, journal, defaults, setCorsHeaders);
|
|
2129
|
+
} catch (err) {
|
|
2130
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2131
|
+
}
|
|
2132
|
+
return;
|
|
2133
|
+
}
|
|
2134
|
+
if (vsIdMatch && req.method === "DELETE") {
|
|
2135
|
+
try {
|
|
2136
|
+
await handleVectorStoresDelete(req, res, vsIdMatch[1], journal, defaults, setCorsHeaders);
|
|
2137
|
+
} catch (err) {
|
|
2138
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2139
|
+
}
|
|
2140
|
+
return;
|
|
2141
|
+
}
|
|
2142
|
+
if (VECTOR_STORES_FILES_RE.test(pathname) && req.method === "POST") {
|
|
2143
|
+
try {
|
|
2144
|
+
const raw = await readBody(req);
|
|
2145
|
+
const storeId = pathname.split("/")[3];
|
|
2146
|
+
await handleVectorStoreFilesCreate(req, res, storeId, raw, journal, defaults, setCorsHeaders);
|
|
2147
|
+
} catch (err) {
|
|
2148
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2149
|
+
}
|
|
2150
|
+
return;
|
|
2151
|
+
}
|
|
2152
|
+
if (VECTOR_STORES_FILES_RE.test(pathname) && req.method === "GET") {
|
|
2153
|
+
try {
|
|
2154
|
+
const storeId = pathname.split("/")[3];
|
|
2155
|
+
await handleVectorStoreFilesList(req, res, storeId, journal, defaults, setCorsHeaders);
|
|
2156
|
+
} catch (err) {
|
|
2157
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2158
|
+
}
|
|
2159
|
+
return;
|
|
2160
|
+
}
|
|
2161
|
+
if (VECTOR_STORES_FILE_BATCHES_RE.test(pathname) && req.method === "POST") {
|
|
2162
|
+
try {
|
|
2163
|
+
const raw = await readBody(req);
|
|
2164
|
+
const storeId = pathname.split("/")[3];
|
|
2165
|
+
await handleVectorFileBatchesCreate(req, res, storeId, raw, journal, defaults, setCorsHeaders);
|
|
2166
|
+
} catch (err) {
|
|
2167
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2168
|
+
}
|
|
2169
|
+
return;
|
|
2170
|
+
}
|
|
2171
|
+
if (pathname === VECTOR_STORES_PATH && req.method === "GET") {
|
|
2172
|
+
try {
|
|
2173
|
+
await handleVectorStoresList(req, res, journal, defaults, setCorsHeaders);
|
|
2174
|
+
} catch (err) {
|
|
2175
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2176
|
+
}
|
|
2177
|
+
return;
|
|
2178
|
+
}
|
|
2179
|
+
if (pathname === VECTOR_STORES_PATH && req.method === "POST") {
|
|
2180
|
+
try {
|
|
2181
|
+
await handleVectorStoresCreate(req, res, await readBody(req), journal, defaults, setCorsHeaders);
|
|
2182
|
+
} catch (err) {
|
|
2183
|
+
routeError(req, res, err, pathname, { service: "vector-stores" });
|
|
2184
|
+
}
|
|
2185
|
+
return;
|
|
2186
|
+
}
|
|
1800
2187
|
if (pathname === REQUESTS_PATH) {
|
|
1801
2188
|
setCorsHeaders(res);
|
|
1802
2189
|
if (req.method === "GET") {
|
|
1803
2190
|
const limitParam = parsedUrl.searchParams.get("limit");
|
|
1804
2191
|
let opts;
|
|
1805
2192
|
if (limitParam) {
|
|
1806
|
-
const limit =
|
|
1807
|
-
if (Number.
|
|
2193
|
+
const limit = Number(limitParam);
|
|
2194
|
+
if (!/^\+?\d+$/.test(limitParam.trim()) || !Number.isSafeInteger(limit) || limit <= 0) {
|
|
1808
2195
|
writeErrorResponse(res, 400, JSON.stringify({ error: {
|
|
1809
2196
|
message: `Invalid limit parameter: "${limitParam}"`,
|
|
1810
2197
|
type: "invalid_request_error"
|
|
@@ -1873,7 +2260,7 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
1873
2260
|
} catch (err) {
|
|
1874
2261
|
if (!(err instanceof SyntaxError)) defaults.logger.error(`Unexpected error in Azure model injection: ${err instanceof Error ? err.message : String(err)}`);
|
|
1875
2262
|
}
|
|
1876
|
-
await handleEmbeddings(req, res, raw, fixtures, journal, defaults, setCorsHeaders, embeddingsProvider);
|
|
2263
|
+
await handleEmbeddings(req, res, raw, fixtures, journal, defaults, setCorsHeaders, embeddingsProvider, originalPathname === EMBEDDINGS_PATH);
|
|
1877
2264
|
} catch (err) {
|
|
1878
2265
|
routeError(req, res, err, pathname);
|
|
1879
2266
|
}
|
|
@@ -2244,6 +2631,8 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
2244
2631
|
return;
|
|
2245
2632
|
}
|
|
2246
2633
|
if (pathname !== COMPLETIONS_PATH) {
|
|
2634
|
+
const catalogMatch = matchRouteDefinition(req.method ?? "", pathname);
|
|
2635
|
+
if (catalogMatch && !(pathname === METRICS_PATH && !registry)) defaults.logger.warn(`Route drift: ${req.method ?? "?"} ${pathname} matches the route catalog (${catalogMatch.method} ${catalogMatch.path} — ${catalogMatch.description}) but no dispatcher branch served it. The catalog entry or the dispatcher is stale — see route-registry.ts.`);
|
|
2247
2636
|
handleNotFound(res, "Not found");
|
|
2248
2637
|
return;
|
|
2249
2638
|
}
|
|
@@ -2323,17 +2712,23 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
2323
2712
|
server.emit("connection", socket);
|
|
2324
2713
|
return;
|
|
2325
2714
|
}
|
|
2326
|
-
|
|
2715
|
+
let parsedUrl;
|
|
2716
|
+
try {
|
|
2717
|
+
parsedUrl = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
2718
|
+
} catch (err) {
|
|
2719
|
+
const msg = err instanceof Error ? err.message : "Internal error";
|
|
2720
|
+
defaults.logger.warn(`Unhandled upgrade error: ${msg}`);
|
|
2721
|
+
socket.end("HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n");
|
|
2722
|
+
return;
|
|
2723
|
+
}
|
|
2327
2724
|
let pathname = parsedUrl.pathname;
|
|
2328
2725
|
if (!validateRequestApiKey(req, resolvedAuth.policy).ok) {
|
|
2329
2726
|
writeApiKeyUpgradeRejection(socket);
|
|
2330
2727
|
return;
|
|
2331
2728
|
}
|
|
2332
|
-
if (
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
if (await handler.handleUpgrade(socket, head, subPath)) return;
|
|
2336
|
-
}
|
|
2729
|
+
for (const { path: mountPath, handler } of mountList) if ((pathname === mountPath || pathname.startsWith(mountPath + "/")) && handler.handleUpgrade) {
|
|
2730
|
+
const subPath = pathname.slice(mountPath.length) || "/";
|
|
2731
|
+
if (await handler.handleUpgrade(socket, head, subPath)) return;
|
|
2337
2732
|
}
|
|
2338
2733
|
if (!pathname.match(AZURE_DEPLOYMENT_RE)) pathname = normalizeCompatPath(pathname, logger);
|
|
2339
2734
|
if (pathname !== RESPONSES_PATH && pathname !== REALTIME_PATH && pathname !== GEMINI_LIVE_PATH && pathname !== LIVE_PATH) {
|
|
@@ -2443,9 +2838,13 @@ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mou
|
|
|
2443
2838
|
return;
|
|
2444
2839
|
}
|
|
2445
2840
|
const url = `http://${addr.address}:${addr.port}`;
|
|
2446
|
-
|
|
2447
|
-
|
|
2841
|
+
try {
|
|
2842
|
+
commitHandOff();
|
|
2843
|
+
} catch (err) {
|
|
2844
|
+
server.close(() => reject(err));
|
|
2845
|
+
return;
|
|
2448
2846
|
}
|
|
2847
|
+
for (const { path: mountPath, handler } of mountList) if (handler.setBaseUrl) handler.setBaseUrl(url + mountPath);
|
|
2449
2848
|
resolve({
|
|
2450
2849
|
closeLiveSessions,
|
|
2451
2850
|
server,
|