@maxceem/agw 0.9.2 → 0.10.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.
Files changed (2) hide show
  1. package/dist/agw.mjs +205 -80
  2. package/package.json +3 -1
package/dist/agw.mjs CHANGED
@@ -6,7 +6,7 @@ var __export = (target, all) => {
6
6
  };
7
7
 
8
8
  // <define:__AGW_MANIFEST__>
9
- var define_AGW_MANIFEST_default = { version: "0.9.2", wrangler: "4.96.0", upgradeFrom: ["0.9.2", "0.9.1", "0.9.0", "0.8.3", "0.8.2", "0.8.1", "0.8.0", "0.7.2", "0.7.1", "0.7.0", "0.6.4", "0.6.3", "0.6.2", "0.6.1", "0.6.0", "0.5.2", "0.5.1", "0.5.0", "0.4.0"], release: { url: "https://github.com/maxceem/app-ai-gateway/releases/download/v0.9.2/gateway-0.9.2.tar.gz", sha256: "995ff36403a45792e1b5d9b3201d54730063359a5b04a2623127f748b0c8b338", manifest: "3a242d93b32b70a1ae19145b1844ba620e5b09ba31c854e56dd723c04e67c9f9" } };
9
+ var define_AGW_MANIFEST_default = { version: "0.10.0", wrangler: "4.96.0", upgradeFrom: ["0.10.0", "0.9.3", "0.9.2", "0.9.1", "0.9.0", "0.8.3", "0.8.2", "0.8.1", "0.8.0", "0.7.2", "0.7.1", "0.7.0", "0.6.4", "0.6.3", "0.6.2", "0.6.1", "0.6.0", "0.5.2", "0.5.1", "0.5.0", "0.4.0"], release: { url: "https://github.com/maxceem/app-ai-gateway/releases/download/v0.10.0/gateway-0.10.0.tar.gz", sha256: "07e8f3d3f6c350db35e181062b87041633eae15bfa3cd0c8651c803a065e94e1", manifest: "3f44570ab891b8d386db59a708e43c5e838c7a2af08c8e95befc95cb1931f700" } };
10
10
 
11
11
  // src/main.ts
12
12
  import { Writable as Writable2 } from "node:stream";
@@ -14639,10 +14639,22 @@ var API_STYLES = [
14639
14639
  "anthropic_messages",
14640
14640
  "gemini_native",
14641
14641
  "audio_transcription",
14642
+ "image_generation",
14643
+ "embeddings",
14644
+ "gemini_embedding",
14642
14645
  /** A provider-native operation with no cross-provider contract of its own. */
14643
14646
  "other"
14644
14647
  ];
14645
- var ENDPOINT_API_STYLES = ["responses", "audio_transcription"];
14648
+ var ENDPOINT_API_STYLES = [
14649
+ "responses",
14650
+ "chat_completions",
14651
+ "anthropic_messages",
14652
+ "gemini_native",
14653
+ "audio_transcription",
14654
+ "image_generation",
14655
+ "embeddings",
14656
+ "gemini_embedding"
14657
+ ];
14646
14658
  var OUTPUT_CLAMP_STYLES = [
14647
14659
  "responses",
14648
14660
  "chat_completions",
@@ -14662,7 +14674,11 @@ var VERCEL_API_STYLES = [
14662
14674
  "chat_completions",
14663
14675
  "anthropic_messages"
14664
14676
  ];
14665
- var VERCEL_ENDPOINT_STYLES = ["responses"];
14677
+ var VERCEL_ENDPOINT_STYLES = [
14678
+ "responses",
14679
+ "chat_completions",
14680
+ "anthropic_messages"
14681
+ ];
14666
14682
  function vercelRoute(modelPrefix) {
14667
14683
  return {
14668
14684
  modelPrefix,
@@ -14684,6 +14700,107 @@ var VERCEL_ROUTES = {
14684
14700
  moonshot: vercelRoute("moonshotai/")
14685
14701
  };
14686
14702
 
14703
+ // ../src/shared/records.ts
14704
+ function asRecord(value) {
14705
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
14706
+ }
14707
+
14708
+ // ../src/shared/model-kinds.ts
14709
+ var MODEL_KINDS = ["text", "image", "speech", "transcription", "embedding", "realtime"];
14710
+ var ENDPOINT_KINDS = [
14711
+ "text",
14712
+ "image",
14713
+ "speech",
14714
+ "transcription",
14715
+ "embedding"
14716
+ ];
14717
+
14718
+ // ../src/shared/protocols.ts
14719
+ var PROTOCOLS = {
14720
+ audio_transcription: {
14721
+ style: "audio_transcription",
14722
+ path: /^(?:(?:v1|openai\/v1)\/audio\/transcriptions|v1\/stt)$/u,
14723
+ clamp: "none",
14724
+ usage: "audio",
14725
+ body: "multipart",
14726
+ kinds: ["transcription"]
14727
+ },
14728
+ chat_completions: {
14729
+ style: "chat_completions",
14730
+ path: /^(?:chat|v1\/chat|openai\/v1\/chat|inference\/v1\/chat|v1beta\/openai\/chat)\/completions$/u,
14731
+ clamp: "chat_completions",
14732
+ usage: "openai",
14733
+ body: "json",
14734
+ kinds: ["text"]
14735
+ },
14736
+ responses: {
14737
+ style: "responses",
14738
+ path: /^(?:v1|openai\/v1)\/responses$/u,
14739
+ clamp: "responses",
14740
+ usage: "openai",
14741
+ body: "json",
14742
+ kinds: ["text"]
14743
+ },
14744
+ gemini_native: {
14745
+ style: "gemini_native",
14746
+ path: /^v1(?:alpha|beta)?\/models\/(?<model>[^/]+):(?:generateContent|streamGenerateContent)$/du,
14747
+ clamp: "gemini_native",
14748
+ usage: "gemini",
14749
+ body: "json",
14750
+ // Image and speech models answer on `generateContent` like text ones.
14751
+ kinds: ["text", "image", "speech"],
14752
+ urlStreaming: { query: "alt=sse", from: ":generateContent", to: ":streamGenerateContent" }
14753
+ },
14754
+ anthropic_messages: {
14755
+ style: "anthropic_messages",
14756
+ path: /^v1\/messages$/u,
14757
+ clamp: "native",
14758
+ usage: "anthropic",
14759
+ body: "json",
14760
+ kinds: ["text"],
14761
+ requiredHeaders: { "anthropic-version": "2023-06-01" }
14762
+ },
14763
+ // Three APIs with no output to cap, each in the exact layouts the descriptors
14764
+ // name: an OpenAI-shaped image or embedding request under `v1/`, ModelArk's
14765
+ // bare `images/generations` and Fireworks' `inference/v1/`, and Gemini's own
14766
+ // embedding method, which like `generateContent` names its model in the URL.
14767
+ image_generation: {
14768
+ style: "image_generation",
14769
+ path: /^(?:v1\/)?images\/generations$/u,
14770
+ clamp: "none",
14771
+ usage: "openai",
14772
+ body: "json",
14773
+ kinds: ["image"]
14774
+ },
14775
+ embeddings: {
14776
+ style: "embeddings",
14777
+ path: /^(?:v1|inference\/v1)\/embeddings$/u,
14778
+ clamp: "none",
14779
+ usage: "openai",
14780
+ body: "json",
14781
+ kinds: ["embedding"]
14782
+ },
14783
+ gemini_embedding: {
14784
+ style: "gemini_embedding",
14785
+ path: /^v1(?:beta)?\/models\/(?<model>[^/]+):embedContent$/du,
14786
+ clamp: "none",
14787
+ usage: "gemini",
14788
+ body: "json",
14789
+ kinds: ["embedding"]
14790
+ },
14791
+ other: {
14792
+ style: "other",
14793
+ path: null,
14794
+ clamp: "native",
14795
+ usage: "unknown",
14796
+ body: "json",
14797
+ kinds: []
14798
+ }
14799
+ };
14800
+ var CLASSIFIED = Object.values(PROTOCOLS).filter(
14801
+ (protocol) => protocol.path !== null
14802
+ );
14803
+
14687
14804
  // ../src/shared/gateways.ts
14688
14805
  var GatewayIdSchema = external_exports.string().trim().min(1).max(100);
14689
14806
  var GATEWAY_DESCRIPTORS = {
@@ -14738,11 +14855,6 @@ function gatewayTypeForCliName(name) {
14738
14855
  return GATEWAY_TYPES.find((type) => GATEWAY_DESCRIPTORS[type].cliName === name);
14739
14856
  }
14740
14857
 
14741
- // ../src/shared/records.ts
14742
- function asRecord(value) {
14743
- return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
14744
- }
14745
-
14746
14858
  // ../src/shared/cost-report.ts
14747
14859
  function finiteNumber(value) {
14748
14860
  return typeof value === "number" && Number.isFinite(value) ? value : null;
@@ -14818,10 +14930,11 @@ var OPENROUTER_COST_REPORT = {
14818
14930
  };
14819
14931
  var XAI_TICKS_PER_USD = 1e10;
14820
14932
  var XAI_COST_REPORT = {
14821
- // Images and videos are provider-native operations, `other` to this gateway.
14822
- // Its Anthropic-compatible Messages API and `v1/stt` are not documented to
14823
- // report a cost, so they keep billing on the catalog.
14824
- styles: ["chat_completions", "responses", "other"],
14933
+ // Videos are provider-native operations, `other` to this gateway. Embeddings
14934
+ // keep the report they were judged by while they were `other` too. Its
14935
+ // Anthropic-compatible Messages API and `v1/stt` are not documented to report
14936
+ // a cost, so they keep billing on the catalog.
14937
+ styles: ["chat_completions", "responses", "image_generation", "embeddings", "other"],
14825
14938
  settled: (report) => report.costUsd !== null,
14826
14939
  readsCredentialSource: false,
14827
14940
  read(root, report) {
@@ -14854,9 +14967,15 @@ var PROVIDER_DESCRIPTORS = {
14854
14967
  // `max_completion_tokens`; every other OpenAI-compatible service still
14855
14968
  // reads the older field.
14856
14969
  chatCompletionsCapField: "max_completion_tokens",
14857
- // The two types whose Responses and transcription request shapes the
14858
- // gateway composes itself for custom endpoints.
14859
- endpointPaths: { responses: "v1/responses", audio_transcription: "v1/audio/transcriptions" },
14970
+ // Text through Responses alone: the `*-pro` and `*-codex` models serve no
14971
+ // other text API, so offering Chat Completions as well would offer them on
14972
+ // a path that refuses them.
14973
+ endpointPaths: {
14974
+ responses: "v1/responses",
14975
+ audio_transcription: "v1/audio/transcriptions",
14976
+ image_generation: "v1/images/generations",
14977
+ embeddings: "v1/embeddings"
14978
+ },
14860
14979
  // Every JSON answer reports `usage`: tokens for the GPT transcription
14861
14980
  // models, `{ type: "duration", seconds }` for whisper-1.
14862
14981
  transcriptionFormats: { metered: ["json", "verbose_json", "diarized_json"], default: "json" },
@@ -14869,6 +14988,7 @@ var PROVIDER_DESCRIPTORS = {
14869
14988
  nativeClampStyle: "anthropic",
14870
14989
  probePath: "v1/models",
14871
14990
  examplePath: "v1/messages",
14991
+ endpointPaths: { anthropic_messages: "v1/messages" },
14872
14992
  modelAuthor: "Anthropic",
14873
14993
  // Anthropic refuses any request without a version header, probe included —
14874
14994
  // and a probe has no client to send one for it. Deliberately not a
@@ -14892,8 +15012,13 @@ var PROVIDER_DESCRIPTORS = {
14892
15012
  ],
14893
15013
  probePath: "v1/models",
14894
15014
  // Native provider paths: xAI transcribes at `v1/stt`, where OpenAI serves
14895
- // `v1/audio/transcriptions`.
14896
- endpointPaths: { responses: "v1/responses", audio_transcription: "v1/stt" },
15015
+ // `v1/audio/transcriptions`. Text through Responses alone, as on OpenAI:
15016
+ // the multi-agent models serve no other text API.
15017
+ endpointPaths: {
15018
+ responses: "v1/responses",
15019
+ audio_transcription: "v1/stt",
15020
+ image_generation: "v1/images/generations"
15021
+ },
14897
15022
  // Every Chat Completions, Responses, image and video answer carries
14898
15023
  // `usage.cost_in_usd_ticks`, what xAI actually billed for it. An image
14899
15024
  // reports nothing else, so this is the only way to bill one; on the token
@@ -14917,6 +15042,11 @@ var PROVIDER_DESCRIPTORS = {
14917
15042
  // Native Gemini generation requests carry the model in the URL rather than
14918
15043
  // in the JSON body, so there is no example path without a model.
14919
15044
  examplePath: "v1beta/models/{model}:generateContent",
15045
+ // Text, image and speech models all answer on `generateContent`.
15046
+ endpointPaths: {
15047
+ gemini_native: "v1beta/models/{model}:generateContent",
15048
+ gemini_embedding: "v1beta/models/{model}:embedContent"
15049
+ },
14920
15050
  modelAuthor: "Google"
14921
15051
  },
14922
15052
  perplexity: {
@@ -14935,6 +15065,7 @@ var PROVIDER_DESCRIPTORS = {
14935
15065
  ],
14936
15066
  // No `probePath`: Perplexity has no unmetered authenticated endpoint.
14937
15067
  examplePath: "chat/completions",
15068
+ endpointPaths: { chat_completions: "chat/completions" },
14938
15069
  modelAuthor: "Perplexity"
14939
15070
  },
14940
15071
  // OpenAI-compatible chat-completions services. `modelAuthor` is set only
@@ -14958,6 +15089,7 @@ var PROVIDER_DESCRIPTORS = {
14958
15089
  // DeepSeek's OpenAI base URL carries no `v1` segment.
14959
15090
  probePath: "models",
14960
15091
  examplePath: "chat/completions",
15092
+ endpointPaths: { chat_completions: "chat/completions" },
14961
15093
  modelAuthor: "DeepSeek"
14962
15094
  },
14963
15095
  groq: {
@@ -14978,7 +15110,11 @@ var PROVIDER_DESCRIPTORS = {
14978
15110
  inferencePaths: ["openai/v1/audio/speech"],
14979
15111
  // Groq's OpenAI-compatible surface is namespaced under `openai/`.
14980
15112
  probePath: "openai/v1/models",
14981
- examplePath: "openai/v1/chat/completions"
15113
+ examplePath: "openai/v1/chat/completions",
15114
+ endpointPaths: {
15115
+ chat_completions: "openai/v1/chat/completions",
15116
+ audio_transcription: "openai/v1/audio/transcriptions"
15117
+ }
14982
15118
  },
14983
15119
  mistral: {
14984
15120
  label: "Mistral",
@@ -14992,6 +15128,7 @@ var PROVIDER_DESCRIPTORS = {
14992
15128
  "v1/audio/speech"
14993
15129
  ],
14994
15130
  probePath: "v1/models",
15131
+ endpointPaths: { chat_completions: "v1/chat/completions" },
14995
15132
  modelAuthor: "Mistral"
14996
15133
  },
14997
15134
  together: {
@@ -15016,7 +15153,8 @@ var PROVIDER_DESCRIPTORS = {
15016
15153
  "v1/audio/speech",
15017
15154
  "v1/embeddings"
15018
15155
  ],
15019
- probePath: "v1/models"
15156
+ probePath: "v1/models",
15157
+ endpointPaths: { chat_completions: "v1/chat/completions" }
15020
15158
  },
15021
15159
  fireworks: {
15022
15160
  label: "Fireworks AI",
@@ -15035,7 +15173,8 @@ var PROVIDER_DESCRIPTORS = {
15035
15173
  // No `probePath`: its list-models call is `v1/accounts/{account}/models`,
15036
15174
  // and the account id cannot be derived from the key. Nothing under
15037
15175
  // `inference/v1/` is documented as a GET.
15038
- examplePath: "inference/v1/chat/completions"
15176
+ examplePath: "inference/v1/chat/completions",
15177
+ endpointPaths: { chat_completions: "inference/v1/chat/completions" }
15039
15178
  },
15040
15179
  cerebras: {
15041
15180
  label: "Cerebras",
@@ -15044,7 +15183,8 @@ var PROVIDER_DESCRIPTORS = {
15044
15183
  nativeClampStyle: "chat_completions",
15045
15184
  // Legacy completions.
15046
15185
  inferencePaths: [{ path: "v1/completions", clamp: "chat_completions" }],
15047
- probePath: "v1/models"
15186
+ probePath: "v1/models",
15187
+ endpointPaths: { chat_completions: "v1/chat/completions" }
15048
15188
  },
15049
15189
  moonshot: {
15050
15190
  label: "Moonshot AI",
@@ -15056,6 +15196,7 @@ var PROVIDER_DESCRIPTORS = {
15056
15196
  // The Anthropic-format API.
15057
15197
  inferencePaths: [{ path: "anthropic/v1/messages", clamp: "anthropic" }],
15058
15198
  probePath: "v1/models",
15199
+ endpointPaths: { chat_completions: "v1/chat/completions" },
15059
15200
  modelAuthor: "Moonshot AI"
15060
15201
  },
15061
15202
  huggingface: {
@@ -15067,11 +15208,12 @@ var PROVIDER_DESCRIPTORS = {
15067
15208
  // catalog section for that reason; see `catalogPrice` in usage.ts.
15068
15209
  directBaseUrl: "https://router.huggingface.co/",
15069
15210
  auth: { header: "authorization", scheme: "Bearer " },
15070
- nativeClampStyle: "chat_completions"
15211
+ nativeClampStyle: "chat_completions",
15071
15212
  // No `probePath`: `router.huggingface.co/v1/models` is public — it answers
15072
15213
  // 200 to a garbage token, so probing it would validate every key. The
15073
15214
  // endpoint that does check a token lives on a different origin
15074
15215
  // (`huggingface.co`), which a path under this base URL cannot express.
15216
+ endpointPaths: { chat_completions: "v1/chat/completions" }
15075
15217
  },
15076
15218
  baseten: {
15077
15219
  label: "Baseten",
@@ -15080,7 +15222,8 @@ var PROVIDER_DESCRIPTORS = {
15080
15222
  directBaseUrl: "https://inference.baseten.co/",
15081
15223
  auth: { header: "authorization", scheme: "Bearer " },
15082
15224
  nativeClampStyle: "chat_completions",
15083
- probePath: "v1/models"
15225
+ probePath: "v1/models",
15226
+ endpointPaths: { chat_completions: "v1/chat/completions" }
15084
15227
  },
15085
15228
  bytedance: {
15086
15229
  label: "ByteDance Ark",
@@ -15102,7 +15245,8 @@ var PROVIDER_DESCRIPTORS = {
15102
15245
  // No `probePath`: ModelArk publishes no list-models call, and its own SDK
15103
15246
  // has no models resource. It also authenticates before it routes, so every
15104
15247
  // path answers the same 401 and a probe would prove nothing about the key.
15105
- examplePath: "chat/completions"
15248
+ examplePath: "chat/completions",
15249
+ endpointPaths: { chat_completions: "chat/completions", image_generation: "images/generations" }
15106
15250
  },
15107
15251
  openrouter: {
15108
15252
  label: "OpenRouter",
@@ -15132,6 +15276,7 @@ var PROVIDER_DESCRIPTORS = {
15132
15276
  // every lab. The declaration owns the parsing and the header that asks for
15133
15277
  // the routing metadata — see `src/shared/cost-report.ts`.
15134
15278
  costReport: OPENROUTER_COST_REPORT,
15279
+ endpointPaths: { chat_completions: "v1/chat/completions" },
15135
15280
  // No `modelAuthor`: an aggregator serves everyone's models, and the slug
15136
15281
  // namespace answers per model instead.
15137
15282
  authorNamespacedModels: true
@@ -15358,12 +15503,33 @@ var EndpointTargetSchema = external_exports.object({
15358
15503
  provider: SlugSchema,
15359
15504
  model: external_exports.string().min(1)
15360
15505
  });
15506
+ function endpointStyleKinds(style2) {
15507
+ return ENDPOINT_KINDS.filter((kind) => PROTOCOLS[style2].kinds.includes(kind));
15508
+ }
15509
+ function endpointKind(endpoint) {
15510
+ return endpoint.kind ?? endpointStyleKinds(endpoint.api_style)[0] ?? "text";
15511
+ }
15361
15512
  var EndpointSchema = EndpointTargetSchema.extend({
15513
+ kind: external_exports.enum(ENDPOINT_KINDS).optional().meta({
15514
+ description: "What the endpoint does, which decides the models it may serve: text, image, speech (text to speech), transcription (speech to text) or embedding. Must be a kind its api_style carries; defaults to the first of them (text for gemini_native). Filled in on every parsed configuration."
15515
+ }),
15362
15516
  api_style: external_exports.enum(ENDPOINT_API_STYLES),
15363
15517
  params: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
15364
15518
  max_output_tokens: external_exports.number().int().positive().optional(),
15365
15519
  fallback: external_exports.array(EndpointTargetSchema).optional()
15366
- }).strict();
15520
+ }).strict().superRefine((endpoint, context) => {
15521
+ const kinds = endpointStyleKinds(endpoint.api_style);
15522
+ if (endpoint.kind !== void 0 && !kinds.includes(endpoint.kind)) {
15523
+ context.addIssue({
15524
+ code: "custom",
15525
+ path: ["kind"],
15526
+ message: `cannot be ${endpoint.kind}: a ${endpoint.api_style} endpoint serves ${new Intl.ListFormat("en", { type: "disjunction" }).format(kinds)}`
15527
+ });
15528
+ }
15529
+ }).overwrite((endpoint) => {
15530
+ endpoint.kind = endpointKind(endpoint);
15531
+ return endpoint;
15532
+ });
15367
15533
  var LimitScopeSchema = external_exports.object({
15368
15534
  requests: external_exports.object({
15369
15535
  per_minute: NullableRequestLimit,
@@ -16856,62 +17022,13 @@ var DEFAULT_INFERENCE_PATHS = new Map(
16856
17022
  ])
16857
17023
  );
16858
17024
 
16859
- // ../src/shared/protocols.ts
16860
- var PROTOCOLS = {
16861
- audio_transcription: {
16862
- style: "audio_transcription",
16863
- path: /^(?:(?:v1|openai\/v1)\/audio\/transcriptions|v1\/stt)$/u,
16864
- clamp: "none",
16865
- usage: "audio",
16866
- body: "multipart"
16867
- },
16868
- chat_completions: {
16869
- style: "chat_completions",
16870
- path: /^(?:chat|v1\/chat|openai\/v1\/chat|inference\/v1\/chat|v1beta\/openai\/chat)\/completions$/u,
16871
- clamp: "chat_completions",
16872
- usage: "openai",
16873
- body: "json"
16874
- },
16875
- responses: {
16876
- style: "responses",
16877
- path: /^(?:v1|openai\/v1)\/responses$/u,
16878
- clamp: "responses",
16879
- usage: "openai",
16880
- body: "json"
16881
- },
16882
- gemini_native: {
16883
- style: "gemini_native",
16884
- path: /^v1(?:alpha|beta)?\/models\/(?<model>[^/]+):(?:generateContent|streamGenerateContent)$/du,
16885
- clamp: "gemini_native",
16886
- usage: "gemini",
16887
- body: "json"
16888
- },
16889
- anthropic_messages: {
16890
- style: "anthropic_messages",
16891
- path: /^v1\/messages$/u,
16892
- clamp: "native",
16893
- usage: "anthropic",
16894
- body: "json"
16895
- },
16896
- other: {
16897
- style: "other",
16898
- path: null,
16899
- clamp: "native",
16900
- usage: "unknown",
16901
- body: "json"
16902
- }
16903
- };
16904
- var CLASSIFIED = Object.values(PROTOCOLS).filter(
16905
- (protocol) => protocol.path !== null
16906
- );
16907
-
16908
17025
  // ../src/shared/first-request.ts
16909
17026
  var PROVIDER_PLACEHOLDER = "PROVIDER_SLUG";
16910
17027
  var MODEL_PLACEHOLDER = "MODEL";
16911
17028
  var BODY_PLACEHOLDER = "REQUEST_BODY";
16912
17029
  var EXAMPLE_GAP_NOTES = {
16913
17030
  provider: `No provider is configured yet, so ${PROVIDER_PLACEHOLDER} stands in for one. Add a provider key, then use its slug.`,
16914
- model: `No priced model was found for this provider, so ${MODEL_PLACEHOLDER} stands in for one. Use a model the provider serves.`,
17031
+ model: `No priced model this request can use was found for this provider, so ${MODEL_PLACEHOLDER} stands in for one. Use a model the provider serves.`,
16915
17032
  body: `This app allows no path whose request shape is known here, so ${BODY_PLACEHOLDER} stands in for the body the provider documents.`
16916
17033
  };
16917
17034
  function shellQuote(value) {
@@ -17131,7 +17248,7 @@ ${snippet}`;
17131
17248
  ...snippet ? { snippet } : {},
17132
17249
  app: app2,
17133
17250
  ...key ? { applicationKey: key } : {},
17134
- guidance: doc.config.authentication.type === "apple_app_attest" ? `Add https://github.com/maxceem/app-ai-gateway-swift from 1.5.0, enable App Attest, and test on a supported physical device. Run agw app snippet ${app2.id} --provider <slug> for integration code. Production releases should use production-only App Attest.` : `Store the generated key on your server; never embed it in a mobile application. Run agw app snippet ${app2.id} for this request again.`
17251
+ guidance: doc.config.authentication.type === "apple_app_attest" ? `Add https://github.com/maxceem/app-ai-gateway-swift from 1.6.0, enable App Attest, and test on a supported physical device. Run agw app snippet ${app2.id} --provider <slug> for integration code. Production releases should use production-only App Attest.` : `Store the generated key on your server; never embed it in a mobile application. Run agw app snippet ${app2.id} for this request again.`
17135
17252
  };
17136
17253
  }
17137
17254
  }
@@ -18108,6 +18225,9 @@ var UsageRepriceResponseSchema = external_exports.object({
18108
18225
  delta_usd: external_exports.number()
18109
18226
  });
18110
18227
  var ModelPriceSchema = external_exports.object({
18228
+ kind: external_exports.enum(MODEL_KINDS).meta({
18229
+ description: "What the model does: text, image (generation), speech (text to speech), transcription (speech to text), embedding, or realtime (served only over a realtime session). A custom endpoint serves models of its own kind only."
18230
+ }),
18111
18231
  input: external_exports.number().optional(),
18112
18232
  output: external_exports.number().optional(),
18113
18233
  image_input: external_exports.number().optional().meta({
@@ -18485,8 +18605,8 @@ var SignInRequestSchema = external_exports.object({ email: external_exports.emai
18485
18605
  var ProviderNativeBodySchema = external_exports.record(external_exports.string(), external_exports.unknown()).meta({
18486
18606
  description: "Provider-native JSON request. Consult the selected provider's API reference for the exact shape."
18487
18607
  });
18488
- var ResponsesStyleBodySchema = external_exports.record(external_exports.string(), external_exports.unknown()).meta({
18489
- description: "OpenAI Responses API body for endpoints whose api_style is responses. The gateway overwrites model and deep-merges the configured params."
18608
+ var EndpointJsonBodySchema = external_exports.record(external_exports.string(), external_exports.unknown()).meta({
18609
+ description: "The JSON body the endpoint's api_style takes: an OpenAI Responses, Chat Completions, image generation or embeddings body, an Anthropic Messages body, or a Gemini generateContent or embedContent body. The gateway sets the configured model (in the body, or in the URL for the Gemini styles) and deep-merges the configured params."
18490
18610
  });
18491
18611
  var TranscriptionStyleBodySchema = external_exports.object({
18492
18612
  file: external_exports.string().meta({ format: "binary" }),
@@ -18674,16 +18794,21 @@ var CATALOG = {
18674
18794
  path: "/v1/apps/{app}/endpoints/{slug}",
18675
18795
  tags: ["Custom endpoints"],
18676
18796
  summary: "Call a server-configured custom endpoint",
18677
- description: "The endpoint's provider, model, fixed parameters, output cap, and fallback chain come from the application configuration, so you can change models without shipping a client release. Responses-style endpoints accept an OpenAI Responses body; audio_transcription endpoints accept an OpenAI audio transcription multipart body and may omit the model field. The successful response keeps the serving provider's native format and streaming behaviour.",
18797
+ description: "The endpoint's provider, model, fixed parameters, output cap, and fallback chain come from the application configuration, so you can change models without shipping a client release. The body is the one the endpoint's api_style takes, without a model: JSON for every style but audio_transcription, which takes an OpenAI audio transcription multipart body. A gemini_native endpoint streams when the request URL carries alt=sse, as Gemini's own API does; every other style streams as its body asks. The successful response keeps the serving provider's native format and streaming behaviour.",
18678
18798
  security: "gateway",
18679
18799
  params: {
18680
18800
  app: { example: "my-app" },
18681
18801
  slug: { example: "chat", pattern: /^[a-z0-9-]{1,64}$/ }
18682
18802
  },
18803
+ query: external_exports.object({
18804
+ alt: external_exports.literal("sse").optional().meta({
18805
+ description: "gemini_native endpoints only: `sse` streams the response as server-sent events, as Gemini's own API does. Never forwarded upstream."
18806
+ })
18807
+ }),
18683
18808
  headers: GatewayClientHeadersSchema,
18684
18809
  request: {
18685
18810
  content: {
18686
- "application/json": ResponsesStyleBodySchema,
18811
+ "application/json": EndpointJsonBodySchema,
18687
18812
  "multipart/form-data": TranscriptionStyleBodySchema
18688
18813
  }
18689
18814
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maxceem/agw",
3
- "version": "0.9.2",
3
+ "version": "0.10.0",
4
4
  "type": "module",
5
5
  "description": "Manage App AI Gateway from a terminal or coding agent: providers, iOS and server apps, usage, and self-hosted deployment on Cloudflare",
6
6
  "keywords": [
@@ -26,6 +26,8 @@
26
26
  },
27
27
  "bugs": "https://github.com/maxceem/app-ai-gateway/issues",
28
28
  "upgradeFrom": [
29
+ "0.9.3",
30
+ "0.9.2",
29
31
  "0.9.1",
30
32
  "0.9.0",
31
33
  "0.8.3",