@farming-labs/theme 0.1.71 → 0.1.72
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/dist/docs-api.d.mts +4 -1
- package/dist/docs-api.mjs +313 -15
- package/package.json +2 -2
package/dist/docs-api.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ChangelogConfig, DocsAnalyticsConfig, DocsI18nConfig, DocsMcpConfig, DocsSearchConfig, FeedbackConfig, OrderingItem } from "@farming-labs/docs";
|
|
1
|
+
import { ChangelogConfig, DocsAnalyticsConfig, DocsI18nConfig, DocsMcpConfig, DocsObservabilityConfig, DocsSearchConfig, FeedbackConfig, OrderingItem } from "@farming-labs/docs";
|
|
2
2
|
|
|
3
3
|
//#region src/docs-api.d.ts
|
|
4
4
|
interface AIProviderConfig {
|
|
@@ -43,6 +43,8 @@ interface DocsAPIOptions {
|
|
|
43
43
|
search?: boolean | DocsSearchConfig;
|
|
44
44
|
/** Analytics configuration */
|
|
45
45
|
analytics?: boolean | DocsAnalyticsConfig;
|
|
46
|
+
/** Observability configuration for logs, traces, and metrics callbacks. */
|
|
47
|
+
observability?: boolean | DocsObservabilityConfig;
|
|
46
48
|
/** Feedback configuration */
|
|
47
49
|
feedback?: boolean | FeedbackConfig;
|
|
48
50
|
/** MCP configuration used for the agent discovery spec. */
|
|
@@ -59,6 +61,7 @@ interface DocsMCPAPIOptions {
|
|
|
59
61
|
mcp?: boolean | DocsMcpConfig;
|
|
60
62
|
search?: boolean | DocsSearchConfig;
|
|
61
63
|
analytics?: boolean | DocsAnalyticsConfig;
|
|
64
|
+
observability?: boolean | DocsObservabilityConfig;
|
|
62
65
|
}
|
|
63
66
|
/**
|
|
64
67
|
* Create a unified docs API route handler.
|
package/dist/docs-api.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { getNextAppDir } from "./get-app-dir.mjs";
|
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import matter from "gray-matter";
|
|
6
|
-
import { emitDocsAnalyticsEvent, normalizeDocsRelated, performDocsSearch, renderDocsRelatedMarkdownLines, resolveChangelogConfig, resolveDocsI18n, resolveDocsLocale, resolvePageSidebarFolderIndexBehavior, resolveSearchRequestConfig } from "@farming-labs/docs";
|
|
6
|
+
import { createDocsAgentTraceContext, createDocsAgentTraceId, emitDocsAgentTraceEvent, emitDocsAnalyticsEvent, normalizeDocsRelated, performDocsSearch, renderDocsRelatedMarkdownLines, resolveChangelogConfig, resolveDocsI18n, resolveDocsLocale, resolvePageSidebarFolderIndexBehavior, resolveSearchRequestConfig } from "@farming-labs/docs";
|
|
7
7
|
import { createDocsMcpHttpHandler, createFilesystemDocsMcpSource, resolveDocsMcpConfig } from "@farming-labs/docs/server";
|
|
8
8
|
|
|
9
9
|
//#region src/docs-api.ts
|
|
@@ -956,9 +956,74 @@ function resolveModelAndProvider(aiConfig, requestedModelId) {
|
|
|
956
956
|
apiKey
|
|
957
957
|
};
|
|
958
958
|
}
|
|
959
|
-
|
|
959
|
+
function safeUrlOrigin(value) {
|
|
960
|
+
try {
|
|
961
|
+
return new URL(value).origin;
|
|
962
|
+
} catch {
|
|
963
|
+
return value;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
async function handleAskAI(request, indexes, aiConfig, analytics, observability, analyticsContext = {}) {
|
|
960
967
|
const url = new URL(request.url);
|
|
961
968
|
const requestStartedAt = Date.now();
|
|
969
|
+
const trace = createDocsAgentTraceContext("ask-ai");
|
|
970
|
+
const runSpanId = createDocsAgentTraceId("span");
|
|
971
|
+
const traceBase = {
|
|
972
|
+
source: "server",
|
|
973
|
+
traceId: trace.traceId,
|
|
974
|
+
url: request.url,
|
|
975
|
+
path: url.pathname,
|
|
976
|
+
locale: analyticsContext.locale
|
|
977
|
+
};
|
|
978
|
+
async function emitTrace(event) {
|
|
979
|
+
await emitDocsAgentTraceEvent(observability, {
|
|
980
|
+
...traceBase,
|
|
981
|
+
...event
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
async function emitRunError(reason, outputPreview = {}) {
|
|
985
|
+
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
986
|
+
const elapsed = Math.max(0, Date.now() - requestStartedAt);
|
|
987
|
+
const common = {
|
|
988
|
+
name: "ask-ai",
|
|
989
|
+
startedAt: trace.startedAt,
|
|
990
|
+
endedAt,
|
|
991
|
+
durationMs: elapsed,
|
|
992
|
+
status: "error",
|
|
993
|
+
outputPreview: {
|
|
994
|
+
reason,
|
|
995
|
+
...outputPreview
|
|
996
|
+
},
|
|
997
|
+
metadata: { reason }
|
|
998
|
+
};
|
|
999
|
+
await emitTrace({
|
|
1000
|
+
...common,
|
|
1001
|
+
type: "error",
|
|
1002
|
+
parentSpanId: runSpanId
|
|
1003
|
+
});
|
|
1004
|
+
await emitTrace({
|
|
1005
|
+
...common,
|
|
1006
|
+
type: "run.error",
|
|
1007
|
+
spanId: runSpanId
|
|
1008
|
+
});
|
|
1009
|
+
await emitTrace({
|
|
1010
|
+
...common,
|
|
1011
|
+
type: "run.end",
|
|
1012
|
+
spanId: runSpanId
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
await emitTrace({
|
|
1016
|
+
type: "run.start",
|
|
1017
|
+
name: "ask-ai",
|
|
1018
|
+
spanId: runSpanId,
|
|
1019
|
+
startedAt: trace.startedAt,
|
|
1020
|
+
durationMs: 0,
|
|
1021
|
+
status: "started",
|
|
1022
|
+
inputPreview: {
|
|
1023
|
+
method: request.method,
|
|
1024
|
+
path: url.pathname
|
|
1025
|
+
}
|
|
1026
|
+
});
|
|
962
1027
|
let body;
|
|
963
1028
|
try {
|
|
964
1029
|
body = await request.json();
|
|
@@ -974,6 +1039,7 @@ async function handleAskAI(request, indexes, aiConfig, analytics, analyticsConte
|
|
|
974
1039
|
durationMs: Math.max(0, Date.now() - requestStartedAt)
|
|
975
1040
|
}
|
|
976
1041
|
});
|
|
1042
|
+
await emitRunError("invalid_json", { status: 400 });
|
|
977
1043
|
return Response.json({ error: "Invalid JSON body. Expected { messages: [...] }" }, { status: 400 });
|
|
978
1044
|
}
|
|
979
1045
|
const messages = body.messages;
|
|
@@ -989,6 +1055,7 @@ async function handleAskAI(request, indexes, aiConfig, analytics, analyticsConte
|
|
|
989
1055
|
durationMs: Math.max(0, Date.now() - requestStartedAt)
|
|
990
1056
|
}
|
|
991
1057
|
});
|
|
1058
|
+
await emitRunError("missing_messages", { status: 400 });
|
|
992
1059
|
return Response.json({ error: "messages array is required and must not be empty." }, { status: 400 });
|
|
993
1060
|
}
|
|
994
1061
|
const lastUserMessage = [...messages].reverse().find((m) => m.role === "user");
|
|
@@ -1005,10 +1072,44 @@ async function handleAskAI(request, indexes, aiConfig, analytics, analyticsConte
|
|
|
1005
1072
|
durationMs: Math.max(0, Date.now() - requestStartedAt)
|
|
1006
1073
|
}
|
|
1007
1074
|
});
|
|
1075
|
+
await emitRunError("missing_user_message", {
|
|
1076
|
+
status: 400,
|
|
1077
|
+
messageCount: messages.length
|
|
1078
|
+
});
|
|
1008
1079
|
return Response.json({ error: "At least one user message is required." }, { status: 400 });
|
|
1009
1080
|
}
|
|
1010
1081
|
const maxResults = aiConfig.maxResults ?? 5;
|
|
1011
1082
|
const query = lastUserMessage.content;
|
|
1083
|
+
await emitTrace({
|
|
1084
|
+
type: "user.input",
|
|
1085
|
+
name: "ask-ai",
|
|
1086
|
+
parentSpanId: runSpanId,
|
|
1087
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1088
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1089
|
+
durationMs: 0,
|
|
1090
|
+
status: "success",
|
|
1091
|
+
inputPreview: {
|
|
1092
|
+
messageCount: messages.length,
|
|
1093
|
+
questionLength: query.length,
|
|
1094
|
+
requestedModel: typeof body.model === "string" && body.model.trim().length > 0 ? body.model.trim() : void 0
|
|
1095
|
+
}
|
|
1096
|
+
});
|
|
1097
|
+
const retrievalStartedAt = Date.now();
|
|
1098
|
+
const retrievalStartedAtIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
1099
|
+
const retrievalSpanId = createDocsAgentTraceId("span");
|
|
1100
|
+
await emitTrace({
|
|
1101
|
+
type: "retrieval.query",
|
|
1102
|
+
name: "docs-index",
|
|
1103
|
+
spanId: retrievalSpanId,
|
|
1104
|
+
parentSpanId: runSpanId,
|
|
1105
|
+
startedAt: retrievalStartedAtIso,
|
|
1106
|
+
status: "started",
|
|
1107
|
+
inputPreview: {
|
|
1108
|
+
queryLength: query.length,
|
|
1109
|
+
maxResults,
|
|
1110
|
+
indexSize: indexes.length
|
|
1111
|
+
}
|
|
1112
|
+
});
|
|
1012
1113
|
const scored = indexes.map((doc) => {
|
|
1013
1114
|
const q = query.toLowerCase();
|
|
1014
1115
|
const titleMatch = doc.title.toLowerCase().includes(q) ? 10 : 0;
|
|
@@ -1020,12 +1121,52 @@ async function handleAskAI(request, indexes, aiConfig, analytics, analyticsConte
|
|
|
1020
1121
|
score: titleMatch + contentMatch
|
|
1021
1122
|
};
|
|
1022
1123
|
}).filter((d) => d.score > 0).sort((a, b) => b.score - a.score).slice(0, maxResults);
|
|
1124
|
+
await emitTrace({
|
|
1125
|
+
type: "retrieval.result",
|
|
1126
|
+
name: "docs-index",
|
|
1127
|
+
parentSpanId: retrievalSpanId,
|
|
1128
|
+
startedAt: retrievalStartedAtIso,
|
|
1129
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1130
|
+
durationMs: Math.max(0, Date.now() - retrievalStartedAt),
|
|
1131
|
+
status: "success",
|
|
1132
|
+
outputPreview: {
|
|
1133
|
+
resultCount: scored.length,
|
|
1134
|
+
urls: scored.slice(0, 5).map((doc) => doc.url)
|
|
1135
|
+
},
|
|
1136
|
+
metadata: {
|
|
1137
|
+
maxResults,
|
|
1138
|
+
indexSize: indexes.length
|
|
1139
|
+
}
|
|
1140
|
+
});
|
|
1141
|
+
const promptStartedAt = Date.now();
|
|
1142
|
+
const promptStartedAtIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
1143
|
+
const promptSpanId = createDocsAgentTraceId("span");
|
|
1023
1144
|
const context = scored.map((doc) => `## ${doc.title}\nURL: ${doc.url}\n${doc.description ? `Description: ${doc.description}\n` : ""}\n${doc.content}`).join("\n\n---\n\n");
|
|
1024
1145
|
const systemPrompt = aiConfig.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
|
|
1025
|
-
const
|
|
1146
|
+
const systemMessage = {
|
|
1026
1147
|
role: "system",
|
|
1027
1148
|
content: context ? `${systemPrompt}\n\n---\n\nDocumentation context:\n\n${context}` : systemPrompt
|
|
1028
|
-
}
|
|
1149
|
+
};
|
|
1150
|
+
const llmMessages = [systemMessage, ...messages.filter((m) => m.role !== "system")];
|
|
1151
|
+
await emitTrace({
|
|
1152
|
+
type: "prompt.build",
|
|
1153
|
+
name: "ask-ai.prompt",
|
|
1154
|
+
spanId: promptSpanId,
|
|
1155
|
+
parentSpanId: runSpanId,
|
|
1156
|
+
startedAt: promptStartedAtIso,
|
|
1157
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1158
|
+
durationMs: Math.max(0, Date.now() - promptStartedAt),
|
|
1159
|
+
status: "success",
|
|
1160
|
+
inputPreview: {
|
|
1161
|
+
messageCount: messages.length,
|
|
1162
|
+
retrievedCount: scored.length
|
|
1163
|
+
},
|
|
1164
|
+
outputPreview: {
|
|
1165
|
+
llmMessageCount: llmMessages.length,
|
|
1166
|
+
contextChars: context.length,
|
|
1167
|
+
systemMessageChars: systemMessage.content.length
|
|
1168
|
+
}
|
|
1169
|
+
});
|
|
1029
1170
|
const resolved = resolveModelAndProvider(aiConfig, typeof body.model === "string" && body.model.trim().length > 0 ? body.model.trim() : void 0);
|
|
1030
1171
|
if (!resolved.apiKey) {
|
|
1031
1172
|
await emitDocsAnalyticsEvent(analytics, {
|
|
@@ -1044,6 +1185,13 @@ async function handleAskAI(request, indexes, aiConfig, analytics, analyticsConte
|
|
|
1044
1185
|
durationMs: Math.max(0, Date.now() - requestStartedAt)
|
|
1045
1186
|
}
|
|
1046
1187
|
});
|
|
1188
|
+
await emitRunError("missing_api_key", {
|
|
1189
|
+
status: 500,
|
|
1190
|
+
messageCount: messages.length,
|
|
1191
|
+
questionLength: query.length,
|
|
1192
|
+
retrievedCount: scored.length,
|
|
1193
|
+
model: resolved.model
|
|
1194
|
+
});
|
|
1047
1195
|
return Response.json({ error: `AI is enabled but no API key was found. Either set apiKey in your docs.config ai section, configure a provider, or add OPENAI_API_KEY to your .env.local file.` }, { status: 500 });
|
|
1048
1196
|
}
|
|
1049
1197
|
await emitDocsAnalyticsEvent(analytics, {
|
|
@@ -1060,20 +1208,100 @@ async function handleAskAI(request, indexes, aiConfig, analytics, analyticsConte
|
|
|
1060
1208
|
model: resolved.model
|
|
1061
1209
|
}
|
|
1062
1210
|
});
|
|
1063
|
-
const
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1211
|
+
const modelStartedAt = Date.now();
|
|
1212
|
+
const modelStartedAtIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
1213
|
+
const modelSpanId = createDocsAgentTraceId("span");
|
|
1214
|
+
const providerOrigin = safeUrlOrigin(resolved.baseUrl);
|
|
1215
|
+
await emitTrace({
|
|
1216
|
+
type: "model.call",
|
|
1217
|
+
name: resolved.model,
|
|
1218
|
+
spanId: modelSpanId,
|
|
1219
|
+
parentSpanId: runSpanId,
|
|
1220
|
+
startedAt: modelStartedAtIso,
|
|
1221
|
+
status: "started",
|
|
1222
|
+
inputPreview: {
|
|
1223
|
+
messageCount: llmMessages.length,
|
|
1071
1224
|
stream: true,
|
|
1072
|
-
|
|
1073
|
-
}
|
|
1225
|
+
providerOrigin
|
|
1226
|
+
},
|
|
1227
|
+
metadata: { model: resolved.model }
|
|
1074
1228
|
});
|
|
1229
|
+
let llmResponse;
|
|
1230
|
+
try {
|
|
1231
|
+
llmResponse = await fetch(`${resolved.baseUrl}/chat/completions`, {
|
|
1232
|
+
method: "POST",
|
|
1233
|
+
headers: {
|
|
1234
|
+
"Content-Type": "application/json",
|
|
1235
|
+
Authorization: `Bearer ${resolved.apiKey}`
|
|
1236
|
+
},
|
|
1237
|
+
body: JSON.stringify({
|
|
1238
|
+
model: resolved.model,
|
|
1239
|
+
stream: true,
|
|
1240
|
+
messages: llmMessages
|
|
1241
|
+
})
|
|
1242
|
+
});
|
|
1243
|
+
} catch (error) {
|
|
1244
|
+
const elapsed = Math.max(0, Date.now() - modelStartedAt);
|
|
1245
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1246
|
+
await emitTrace({
|
|
1247
|
+
type: "model.error",
|
|
1248
|
+
name: resolved.model,
|
|
1249
|
+
parentSpanId: modelSpanId,
|
|
1250
|
+
startedAt: modelStartedAtIso,
|
|
1251
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1252
|
+
durationMs: elapsed,
|
|
1253
|
+
status: "error",
|
|
1254
|
+
outputPreview: { message },
|
|
1255
|
+
metadata: {
|
|
1256
|
+
model: resolved.model,
|
|
1257
|
+
providerOrigin
|
|
1258
|
+
}
|
|
1259
|
+
});
|
|
1260
|
+
await emitDocsAnalyticsEvent(analytics, {
|
|
1261
|
+
type: "api_ai_error",
|
|
1262
|
+
source: "server",
|
|
1263
|
+
url: request.url,
|
|
1264
|
+
path: url.pathname,
|
|
1265
|
+
locale: analyticsContext.locale,
|
|
1266
|
+
input: { question: query },
|
|
1267
|
+
properties: {
|
|
1268
|
+
reason: "llm_fetch_error",
|
|
1269
|
+
messageCount: messages.length,
|
|
1270
|
+
questionLength: query.length,
|
|
1271
|
+
retrievedCount: scored.length,
|
|
1272
|
+
model: resolved.model,
|
|
1273
|
+
durationMs: Math.max(0, Date.now() - requestStartedAt)
|
|
1274
|
+
}
|
|
1275
|
+
});
|
|
1276
|
+
await emitRunError("llm_fetch_error", {
|
|
1277
|
+
status: 502,
|
|
1278
|
+
messageCount: messages.length,
|
|
1279
|
+
questionLength: query.length,
|
|
1280
|
+
retrievedCount: scored.length,
|
|
1281
|
+
model: resolved.model
|
|
1282
|
+
});
|
|
1283
|
+
return Response.json({ error: "LLM API request failed." }, { status: 502 });
|
|
1284
|
+
}
|
|
1075
1285
|
if (!llmResponse.ok) {
|
|
1076
1286
|
const errText = await llmResponse.text().catch(() => "Unknown error");
|
|
1287
|
+
const elapsed = Math.max(0, Date.now() - modelStartedAt);
|
|
1288
|
+
await emitTrace({
|
|
1289
|
+
type: "model.error",
|
|
1290
|
+
name: resolved.model,
|
|
1291
|
+
parentSpanId: modelSpanId,
|
|
1292
|
+
startedAt: modelStartedAtIso,
|
|
1293
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1294
|
+
durationMs: elapsed,
|
|
1295
|
+
status: "error",
|
|
1296
|
+
outputPreview: {
|
|
1297
|
+
status: llmResponse.status,
|
|
1298
|
+
errorChars: errText.length
|
|
1299
|
+
},
|
|
1300
|
+
metadata: {
|
|
1301
|
+
model: resolved.model,
|
|
1302
|
+
providerOrigin
|
|
1303
|
+
}
|
|
1304
|
+
});
|
|
1077
1305
|
await emitDocsAnalyticsEvent(analytics, {
|
|
1078
1306
|
type: "api_ai_error",
|
|
1079
1307
|
source: "server",
|
|
@@ -1091,6 +1319,14 @@ async function handleAskAI(request, indexes, aiConfig, analytics, analyticsConte
|
|
|
1091
1319
|
durationMs: Math.max(0, Date.now() - requestStartedAt)
|
|
1092
1320
|
}
|
|
1093
1321
|
});
|
|
1322
|
+
await emitRunError("llm_error", {
|
|
1323
|
+
status: 502,
|
|
1324
|
+
modelStatus: llmResponse.status,
|
|
1325
|
+
messageCount: messages.length,
|
|
1326
|
+
questionLength: query.length,
|
|
1327
|
+
retrievedCount: scored.length,
|
|
1328
|
+
model: resolved.model
|
|
1329
|
+
});
|
|
1094
1330
|
return Response.json({ error: `LLM API error (${llmResponse.status}): ${errText}` }, { status: 502 });
|
|
1095
1331
|
}
|
|
1096
1332
|
await emitDocsAnalyticsEvent(analytics, {
|
|
@@ -1108,6 +1344,66 @@ async function handleAskAI(request, indexes, aiConfig, analytics, analyticsConte
|
|
|
1108
1344
|
durationMs: Math.max(0, Date.now() - requestStartedAt)
|
|
1109
1345
|
}
|
|
1110
1346
|
});
|
|
1347
|
+
const responseEndedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1348
|
+
const modelDurationMs = Math.max(0, Date.now() - modelStartedAt);
|
|
1349
|
+
await emitTrace({
|
|
1350
|
+
type: "model.response",
|
|
1351
|
+
name: resolved.model,
|
|
1352
|
+
parentSpanId: modelSpanId,
|
|
1353
|
+
startedAt: modelStartedAtIso,
|
|
1354
|
+
endedAt: responseEndedAt,
|
|
1355
|
+
durationMs: modelDurationMs,
|
|
1356
|
+
status: "success",
|
|
1357
|
+
outputPreview: {
|
|
1358
|
+
status: llmResponse.status,
|
|
1359
|
+
stream: true,
|
|
1360
|
+
contentType: llmResponse.headers.get("content-type") ?? void 0
|
|
1361
|
+
},
|
|
1362
|
+
metadata: {
|
|
1363
|
+
model: resolved.model,
|
|
1364
|
+
providerOrigin
|
|
1365
|
+
}
|
|
1366
|
+
});
|
|
1367
|
+
await emitTrace({
|
|
1368
|
+
type: "model.stream",
|
|
1369
|
+
name: resolved.model,
|
|
1370
|
+
parentSpanId: modelSpanId,
|
|
1371
|
+
startedAt: modelStartedAtIso,
|
|
1372
|
+
endedAt: responseEndedAt,
|
|
1373
|
+
durationMs: modelDurationMs,
|
|
1374
|
+
status: "success",
|
|
1375
|
+
outputPreview: { stream: true },
|
|
1376
|
+
metadata: { model: resolved.model }
|
|
1377
|
+
});
|
|
1378
|
+
const runDurationMs = Math.max(0, Date.now() - requestStartedAt);
|
|
1379
|
+
await emitTrace({
|
|
1380
|
+
type: "agent.final",
|
|
1381
|
+
name: "ask-ai",
|
|
1382
|
+
parentSpanId: runSpanId,
|
|
1383
|
+
startedAt: trace.startedAt,
|
|
1384
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1385
|
+
durationMs: runDurationMs,
|
|
1386
|
+
status: "success",
|
|
1387
|
+
outputPreview: {
|
|
1388
|
+
stream: true,
|
|
1389
|
+
retrievedCount: scored.length
|
|
1390
|
+
},
|
|
1391
|
+
metadata: { model: resolved.model }
|
|
1392
|
+
});
|
|
1393
|
+
await emitTrace({
|
|
1394
|
+
type: "run.end",
|
|
1395
|
+
name: "ask-ai",
|
|
1396
|
+
spanId: runSpanId,
|
|
1397
|
+
startedAt: trace.startedAt,
|
|
1398
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1399
|
+
durationMs: runDurationMs,
|
|
1400
|
+
status: "success",
|
|
1401
|
+
outputPreview: {
|
|
1402
|
+
stream: true,
|
|
1403
|
+
retrievedCount: scored.length
|
|
1404
|
+
},
|
|
1405
|
+
metadata: { model: resolved.model }
|
|
1406
|
+
});
|
|
1111
1407
|
return new Response(llmResponse.body, { headers: {
|
|
1112
1408
|
"Content-Type": "text/event-stream",
|
|
1113
1409
|
"Cache-Control": "no-cache",
|
|
@@ -1188,6 +1484,7 @@ function createDocsAPI(options) {
|
|
|
1188
1484
|
const root = options?.rootDir ?? process.cwd();
|
|
1189
1485
|
const entry = options?.entry ?? readEntry(root);
|
|
1190
1486
|
const analytics = options?.analytics;
|
|
1487
|
+
const observability = options?.observability;
|
|
1191
1488
|
const appDir = getNextAppDir(root);
|
|
1192
1489
|
const contentDir = options?.contentDir ?? path.join(appDir, entry);
|
|
1193
1490
|
const changelogConfig = resolveChangelogConfig(options?.changelog);
|
|
@@ -1586,7 +1883,7 @@ function createDocsAPI(options) {
|
|
|
1586
1883
|
return Response.json({ error: "AI is not enabled. Set `ai: { enabled: true }` in your docs.config to enable it." }, { status: 404 });
|
|
1587
1884
|
}
|
|
1588
1885
|
const ctx = resolveContextFromRequest(request);
|
|
1589
|
-
return handleAskAI(request, getIndexes(ctx), aiConfig, analytics, { locale: ctx.locale });
|
|
1886
|
+
return handleAskAI(request, getIndexes(ctx), aiConfig, analytics, observability, { locale: ctx.locale });
|
|
1590
1887
|
}
|
|
1591
1888
|
};
|
|
1592
1889
|
}
|
|
@@ -1616,6 +1913,7 @@ function createDocsMCPAPI(options = {}) {
|
|
|
1616
1913
|
mcp: options.mcp ?? readMcpConfig(rootDir),
|
|
1617
1914
|
search: options.search,
|
|
1618
1915
|
analytics: options.analytics,
|
|
1916
|
+
observability: options.observability,
|
|
1619
1917
|
defaultName: navTitle
|
|
1620
1918
|
});
|
|
1621
1919
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farming-labs/theme",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.72",
|
|
4
4
|
"description": "Theme package for @farming-labs/docs — layout, provider, MDX components, and styles",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"docs",
|
|
@@ -139,7 +139,7 @@
|
|
|
139
139
|
"tsdown": "^0.20.3",
|
|
140
140
|
"typescript": "^5.9.3",
|
|
141
141
|
"vitest": "^3.2.4",
|
|
142
|
-
"@farming-labs/docs": "0.1.
|
|
142
|
+
"@farming-labs/docs": "0.1.72"
|
|
143
143
|
},
|
|
144
144
|
"peerDependencies": {
|
|
145
145
|
"@farming-labs/docs": ">=0.0.1",
|