@hebo-ai/gateway 0.5.1 → 0.5.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hebo-ai/gateway",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "AI gateway as a framework. For full control over models, routing & lifecycle. OpenAI-compatible /chat/completions, /embeddings & /models.",
5
5
  "keywords": [
6
6
  "ai",
@@ -36,7 +36,7 @@ import {
36
36
  getChatRequestAttributes,
37
37
  getChatResponseAttributes,
38
38
  } from "./otel";
39
- import { ChatCompletionsBodySchema } from "./schema";
39
+ import { ChatCompletionsBodySchema, type ChatCompletionsBody } from "./schema";
40
40
 
41
41
  export const chatCompletions = (config: GatewayConfig): Endpoint => {
42
42
  const hooks = config.hooks;
@@ -57,6 +57,7 @@ export const chatCompletions = (config: GatewayConfig): Endpoint => {
57
57
  } catch {
58
58
  throw new GatewayError("Invalid JSON", 400);
59
59
  }
60
+ logger.trace({ requestId: ctx.requestId, body: ctx.body }, "[chat] ChatCompletionsBody");
60
61
  addSpanEvent("hebo.request.deserialized");
61
62
 
62
63
  const parsed = ChatCompletionsBodySchema.safeParse(ctx.body);
@@ -68,7 +69,8 @@ export const chatCompletions = (config: GatewayConfig): Endpoint => {
68
69
  addSpanEvent("hebo.request.parsed");
69
70
 
70
71
  if (hooks?.before) {
71
- ctx.body = (await hooks.before(ctx as BeforeHookContext)) ?? ctx.body;
72
+ ctx.body =
73
+ ((await hooks.before(ctx as BeforeHookContext)) as ChatCompletionsBody) ?? ctx.body;
72
74
  addSpanEvent("hebo.hooks.before.completed");
73
75
  }
74
76
 
@@ -110,7 +112,7 @@ export const chatCompletions = (config: GatewayConfig): Endpoint => {
110
112
  "[chat] AI SDK options",
111
113
  );
112
114
  addSpanEvent("hebo.options.prepared");
113
- setSpanAttributes(getChatRequestAttributes(inputs, genAiSignalLevel));
115
+ setSpanAttributes(getChatRequestAttributes(ctx.body, genAiSignalLevel));
114
116
 
115
117
  // Build middleware chain (model -> forward params -> provider).
116
118
  const languageModelWithMiddleware = wrapLanguageModel({
@@ -138,6 +140,10 @@ export const chatCompletions = (config: GatewayConfig): Endpoint => {
138
140
  res as unknown as GenerateTextResult<ToolSet, Output.Output>,
139
141
  ctx.resolvedModelId!,
140
142
  );
143
+ logger.trace(
144
+ { requestId: ctx.requestId, result: streamResult },
145
+ "[chat] ChatCompletions",
146
+ );
141
147
  addSpanEvent("hebo.result.transformed");
142
148
 
143
149
  const genAiResponseAttrs = getChatResponseAttributes(streamResult, genAiSignalLevel);
@@ -180,6 +186,7 @@ export const chatCompletions = (config: GatewayConfig): Endpoint => {
180
186
 
181
187
  // Transform result.
182
188
  ctx.result = toChatCompletions(result, ctx.resolvedModelId);
189
+ logger.trace({ requestId: ctx.requestId, result: ctx.result }, "[chat] ChatCompletions");
183
190
  addSpanEvent("hebo.result.transformed");
184
191
 
185
192
  const genAiResponseAttrs = getChatResponseAttributes(ctx.result, genAiSignalLevel);
@@ -29,7 +29,7 @@ import {
29
29
  getEmbeddingsRequestAttributes,
30
30
  getEmbeddingsResponseAttributes,
31
31
  } from "./otel";
32
- import { EmbeddingsBodySchema } from "./schema";
32
+ import { EmbeddingsBodySchema, type EmbeddingsBody } from "./schema";
33
33
 
34
34
  export const embeddings = (config: GatewayConfig): Endpoint => {
35
35
  const hooks = config.hooks;
@@ -50,6 +50,7 @@ export const embeddings = (config: GatewayConfig): Endpoint => {
50
50
  } catch {
51
51
  throw new GatewayError("Invalid JSON", 400);
52
52
  }
53
+ logger.trace({ requestId: ctx.requestId, result: ctx.body }, "[chat] EmbeddingsBody");
53
54
  addSpanEvent("hebo.request.deserialized");
54
55
 
55
56
  const parsed = EmbeddingsBodySchema.safeParse(ctx.body);
@@ -61,7 +62,7 @@ export const embeddings = (config: GatewayConfig): Endpoint => {
61
62
  addSpanEvent("hebo.request.parsed");
62
63
 
63
64
  if (hooks?.before) {
64
- ctx.body = (await hooks.before(ctx as BeforeHookContext)) ?? ctx.body;
65
+ ctx.body = ((await hooks.before(ctx as BeforeHookContext)) as EmbeddingsBody) ?? ctx.body;
65
66
  addSpanEvent("hebo.hooks.before.completed");
66
67
  }
67
68
 
@@ -100,7 +101,7 @@ export const embeddings = (config: GatewayConfig): Endpoint => {
100
101
  "[embeddings] AI SDK options",
101
102
  );
102
103
  addSpanEvent("hebo.options.prepared");
103
- setSpanAttributes(getEmbeddingsRequestAttributes(inputs, genAiSignalLevel));
104
+ setSpanAttributes(getEmbeddingsRequestAttributes(ctx.body, genAiSignalLevel));
104
105
 
105
106
  // Build middleware chain (model -> forward params -> provider).
106
107
  const embeddingModelWithMiddleware = wrapEmbeddingModel({
@@ -121,6 +122,7 @@ export const embeddings = (config: GatewayConfig): Endpoint => {
121
122
 
122
123
  // Transform result.
123
124
  ctx.result = toEmbeddings(result, ctx.modelId);
125
+ logger.trace({ requestId: ctx.requestId, result: ctx.result }, "[chat] Embeddings");
124
126
  addSpanEvent("hebo.result.transformed");
125
127
  const genAiResponseAttrs = getEmbeddingsResponseAttributes(ctx.result, genAiSignalLevel);
126
128
  recordTokenUsage(genAiResponseAttrs, genAiGeneralAttrs, genAiSignalLevel);
@@ -0,0 +1,37 @@
1
+ import type { EmbeddingModelMiddleware, LanguageModelMiddleware } from "ai";
2
+
3
+ import { logger } from "../logger";
4
+
5
+ export const debugLanguageFinalParamsMiddleware: LanguageModelMiddleware = {
6
+ specificationVersion: "v3",
7
+ // eslint-disable-next-line require-await
8
+ transformParams: async ({ params, model }) => {
9
+ logger.trace(
10
+ {
11
+ kind: "text",
12
+ modelId: model.modelId,
13
+ providerId: model.provider,
14
+ params,
15
+ },
16
+ "[middleware] final params",
17
+ );
18
+ return params;
19
+ },
20
+ };
21
+
22
+ export const debugEmbeddingFinalParamsMiddleware: EmbeddingModelMiddleware = {
23
+ specificationVersion: "v3",
24
+ // eslint-disable-next-line require-await
25
+ transformParams: async ({ params, model }) => {
26
+ logger.trace(
27
+ {
28
+ kind: "embedding",
29
+ modelId: model.modelId,
30
+ providerId: model.provider,
31
+ params,
32
+ },
33
+ "[middleware] final params",
34
+ );
35
+ return params;
36
+ },
37
+ };
@@ -6,6 +6,7 @@ import type { ProviderId } from "../providers/types";
6
6
  import { logger } from "../logger";
7
7
  import { addSpanEvent } from "../telemetry/span";
8
8
  import { forwardParamsEmbeddingMiddleware, forwardParamsMiddleware } from "./common";
9
+ import { debugEmbeddingFinalParamsMiddleware, debugLanguageFinalParamsMiddleware } from "./debug";
9
10
 
10
11
  type MiddlewareEntries = {
11
12
  language?: LanguageModelMiddleware[];
@@ -110,6 +111,9 @@ class ModelMiddlewareMatcher {
110
111
  if (providerId) {
111
112
  out.push(...this.collect(this.provider.match(providerId), kind));
112
113
  }
114
+ out.push(
115
+ kind === "text" ? debugLanguageFinalParamsMiddleware : debugEmbeddingFinalParamsMiddleware,
116
+ );
113
117
 
114
118
  if (this.cache.size >= ModelMiddlewareMatcher.MAX_CACHE) {
115
119
  let n = Math.ceil(ModelMiddlewareMatcher.MAX_CACHE * 0.2);