@farming-labs/svelte 0.1.71 → 0.1.73
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/server.js +314 -28
- package/package.json +2 -2
package/dist/server.js
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
import fs from "node:fs";
|
|
31
31
|
import path from "node:path";
|
|
32
32
|
import matter from "gray-matter";
|
|
33
|
-
import { applySidebarFolderIndexBehavior, buildDocsAgentDiscoverySpec, emitDocsAnalyticsEvent, findDocsMarkdownPage, isDocsAgentDiscoveryRequest, isDocsSkillRequest, normalizeDocsRelated, performDocsSearch, renderDocsMarkdownDocument, renderDocsSkillDocument, stripGeneratedAgentProvenance, resolveDocsAgentMdxContent, resolvePageSidebarFolderIndexBehavior, resolveSearchRequestConfig, resolveDocsI18n, resolveDocsLlmsTxtFormat, resolveDocsLocale, resolveDocsMarkdownRequest, resolveDocsPath, resolvePageReadingTime, resolveReadingTimeOptions, resolveDocsSkillFormat, } from "@farming-labs/docs";
|
|
33
|
+
import { applySidebarFolderIndexBehavior, buildDocsAskAIContext, buildDocsAgentDiscoverySpec, createDocsAgentTraceContext, createDocsAgentTraceId, emitDocsAgentTraceEvent, emitDocsAnalyticsEvent, formatDocsAskAIPackageHints, findDocsMarkdownPage, isDocsAgentDiscoveryRequest, isDocsSkillRequest, normalizeDocsRelated, performDocsSearch, renderDocsMarkdownDocument, renderDocsSkillDocument, stripGeneratedAgentProvenance, resolveDocsAgentMdxContent, resolvePageSidebarFolderIndexBehavior, resolveSearchRequestConfig, resolveDocsI18n, resolveDocsLlmsTxtFormat, resolveDocsLocale, resolveDocsMarkdownRequest, resolveDocsPath, resolvePageReadingTime, resolveReadingTimeOptions, resolveDocsSkillFormat, } from "@farming-labs/docs";
|
|
34
34
|
import { createDocsMcpHttpHandler, resolveDocsMcpConfig, serializeDocsIconRegistry, serializeOpenDocsProviders, } from "@farming-labs/docs/server";
|
|
35
35
|
import { loadDocsNavTree, loadDocsContent, flattenNavTree } from "./content.js";
|
|
36
36
|
import { renderMarkdown } from "./markdown.js";
|
|
@@ -59,6 +59,14 @@ function resolveAIModelAndProvider(aiConfig, requestedModelId) {
|
|
|
59
59
|
(typeof process !== "undefined" ? process.env?.OPENAI_API_KEY : undefined);
|
|
60
60
|
return { model: modelId, baseUrl, apiKey };
|
|
61
61
|
}
|
|
62
|
+
function safeUrlOrigin(value) {
|
|
63
|
+
try {
|
|
64
|
+
return new URL(value).origin;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
62
70
|
function stripMarkdownText(content) {
|
|
63
71
|
return content
|
|
64
72
|
.replace(/^(import|export)\s.*$/gm, "")
|
|
@@ -342,6 +350,7 @@ function findPageInMap(contentMap, dirPrefix, slug) {
|
|
|
342
350
|
export function createDocsServer(config = {}) {
|
|
343
351
|
const entry = config.entry ?? "docs";
|
|
344
352
|
const analytics = config.analytics;
|
|
353
|
+
const observability = config.observability;
|
|
345
354
|
const contentDirBase = config.contentDir ?? entry;
|
|
346
355
|
const rootDir = path.resolve(config.rootDir ?? process.cwd());
|
|
347
356
|
const i18n = resolveDocsI18n(config.i18n);
|
|
@@ -539,20 +548,6 @@ export function createDocsServer(config = {}) {
|
|
|
539
548
|
searchIndexByEntry.set(key, index);
|
|
540
549
|
return index;
|
|
541
550
|
}
|
|
542
|
-
function searchByQuery(query, ctx) {
|
|
543
|
-
const index = getSearchIndex(ctx);
|
|
544
|
-
return index
|
|
545
|
-
.map((page) => {
|
|
546
|
-
const titleMatch = page.title.toLowerCase().includes(query) ? 10 : 0;
|
|
547
|
-
const words = query.split(/\s+/);
|
|
548
|
-
const contentMatch = words.reduce((score, word) => {
|
|
549
|
-
return score + (page.content.toLowerCase().includes(word) ? 1 : 0);
|
|
550
|
-
}, 0);
|
|
551
|
-
return { ...page, score: titleMatch + contentMatch };
|
|
552
|
-
})
|
|
553
|
-
.filter((r) => r.score > 0)
|
|
554
|
-
.sort((a, b) => b.score - a.score);
|
|
555
|
-
}
|
|
556
551
|
// ─── llms.txt content builder ────────────────────────────────
|
|
557
552
|
const llmsSiteTitle = typeof config.nav === "object" &&
|
|
558
553
|
typeof config.nav?.title === "string"
|
|
@@ -739,12 +734,16 @@ export function createDocsServer(config = {}) {
|
|
|
739
734
|
function buildDefaultSystemPrompt() {
|
|
740
735
|
const lines = [
|
|
741
736
|
`You are a helpful documentation assistant${projectName ? ` for ${projectName}` : ""}.`,
|
|
742
|
-
"Answer
|
|
737
|
+
"Answer only from the provided documentation context.",
|
|
738
|
+
"Prefer exact code/config snippets from the context when the question asks how to implement something.",
|
|
739
|
+
"Cite the relevant documentation URL when you use a source.",
|
|
740
|
+
"Use only URLs exactly as they appear in the context; do not invent placeholder domains.",
|
|
741
|
+
'Never use placeholder package names or imports such as "your-auth-library", "your-package", "your-sdk", "replace-me", or "example-library". If the exact package or import is not in the context, do not include an import snippet.',
|
|
743
742
|
"Be concise and accurate. If the answer is not in the context, say so honestly.",
|
|
744
743
|
"Use markdown formatting for code examples and links.",
|
|
745
744
|
];
|
|
746
745
|
if (packageName) {
|
|
747
|
-
lines.push(`When showing import examples,
|
|
746
|
+
lines.push(`When showing import examples, use "${packageName}" as the package name and prefer exact imports copied from the documentation context.`);
|
|
748
747
|
}
|
|
749
748
|
if (docsUrl) {
|
|
750
749
|
lines.push(`When linking to documentation pages, use "${docsUrl}" as the base URL (e.g. ${docsUrl}/docs/get-started).`);
|
|
@@ -755,6 +754,51 @@ export function createDocsServer(config = {}) {
|
|
|
755
754
|
async function POST(event) {
|
|
756
755
|
const requestUrl = new URL(event.request.url);
|
|
757
756
|
const requestStartedAt = Date.now();
|
|
757
|
+
const trace = createDocsAgentTraceContext("ask-ai");
|
|
758
|
+
const runSpanId = createDocsAgentTraceId("span");
|
|
759
|
+
const traceBase = {
|
|
760
|
+
source: "server",
|
|
761
|
+
traceId: trace.traceId,
|
|
762
|
+
url: event.request.url,
|
|
763
|
+
path: requestUrl.pathname,
|
|
764
|
+
};
|
|
765
|
+
async function emitTrace(traceEvent) {
|
|
766
|
+
await emitDocsAgentTraceEvent(observability, {
|
|
767
|
+
...traceBase,
|
|
768
|
+
...traceEvent,
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
async function emitRunError(reason, outputPreview = {}) {
|
|
772
|
+
const endedAt = new Date().toISOString();
|
|
773
|
+
const elapsed = Math.max(0, Date.now() - requestStartedAt);
|
|
774
|
+
const common = {
|
|
775
|
+
name: "ask-ai",
|
|
776
|
+
startedAt: trace.startedAt,
|
|
777
|
+
endedAt,
|
|
778
|
+
durationMs: elapsed,
|
|
779
|
+
status: "error",
|
|
780
|
+
outputPreview: {
|
|
781
|
+
reason,
|
|
782
|
+
...outputPreview,
|
|
783
|
+
},
|
|
784
|
+
metadata: { reason },
|
|
785
|
+
};
|
|
786
|
+
await emitTrace({ ...common, type: "error", parentSpanId: runSpanId });
|
|
787
|
+
await emitTrace({ ...common, type: "run.error", spanId: runSpanId });
|
|
788
|
+
await emitTrace({ ...common, type: "run.end", spanId: runSpanId });
|
|
789
|
+
}
|
|
790
|
+
await emitTrace({
|
|
791
|
+
type: "run.start",
|
|
792
|
+
name: "ask-ai",
|
|
793
|
+
spanId: runSpanId,
|
|
794
|
+
startedAt: trace.startedAt,
|
|
795
|
+
durationMs: 0,
|
|
796
|
+
status: "started",
|
|
797
|
+
inputPreview: {
|
|
798
|
+
method: event.request.method,
|
|
799
|
+
path: requestUrl.pathname,
|
|
800
|
+
},
|
|
801
|
+
});
|
|
758
802
|
if (!aiConfig.enabled) {
|
|
759
803
|
await emitDocsAnalyticsEvent(analytics, {
|
|
760
804
|
type: "api_ai_error",
|
|
@@ -763,6 +807,7 @@ export function createDocsServer(config = {}) {
|
|
|
763
807
|
path: requestUrl.pathname,
|
|
764
808
|
properties: { reason: "disabled" },
|
|
765
809
|
});
|
|
810
|
+
await emitRunError("disabled", { status: 404 });
|
|
766
811
|
return new Response(JSON.stringify({
|
|
767
812
|
error: "AI is not enabled. Set `ai: { enabled: true }` in your docs config to enable it.",
|
|
768
813
|
}), { status: 404, headers: { "Content-Type": "application/json" } });
|
|
@@ -781,6 +826,7 @@ export function createDocsServer(config = {}) {
|
|
|
781
826
|
durationMs: Math.max(0, Date.now() - requestStartedAt),
|
|
782
827
|
},
|
|
783
828
|
});
|
|
829
|
+
await emitRunError("missing_api_key", { status: 500 });
|
|
784
830
|
return new Response(JSON.stringify({
|
|
785
831
|
error: "AI is enabled but no API key was found. Set `apiKey` in your docs config `ai` section or add OPENAI_API_KEY to your environment.",
|
|
786
832
|
}), { status: 500, headers: { "Content-Type": "application/json" } });
|
|
@@ -802,6 +848,7 @@ export function createDocsServer(config = {}) {
|
|
|
802
848
|
durationMs: Math.max(0, Date.now() - requestStartedAt),
|
|
803
849
|
},
|
|
804
850
|
});
|
|
851
|
+
await emitRunError("invalid_json", { status: 400, locale: ctx.locale });
|
|
805
852
|
return new Response(JSON.stringify({ error: "Invalid JSON body. Expected { messages: [...] }" }), { status: 400, headers: { "Content-Type": "application/json" } });
|
|
806
853
|
}
|
|
807
854
|
const messages = body.messages;
|
|
@@ -817,6 +864,7 @@ export function createDocsServer(config = {}) {
|
|
|
817
864
|
durationMs: Math.max(0, Date.now() - requestStartedAt),
|
|
818
865
|
},
|
|
819
866
|
});
|
|
867
|
+
await emitRunError("missing_messages", { status: 400, locale: ctx.locale });
|
|
820
868
|
return new Response(JSON.stringify({ error: "messages array is required and must not be empty." }), { status: 400, headers: { "Content-Type": "application/json" } });
|
|
821
869
|
}
|
|
822
870
|
const lastUserMessage = [...messages].reverse().find((m) => m.role === "user");
|
|
@@ -833,26 +881,113 @@ export function createDocsServer(config = {}) {
|
|
|
833
881
|
durationMs: Math.max(0, Date.now() - requestStartedAt),
|
|
834
882
|
},
|
|
835
883
|
});
|
|
884
|
+
await emitRunError("missing_user_message", {
|
|
885
|
+
status: 400,
|
|
886
|
+
locale: ctx.locale,
|
|
887
|
+
messageCount: messages.length,
|
|
888
|
+
});
|
|
836
889
|
return new Response(JSON.stringify({ error: "At least one user message is required." }), {
|
|
837
890
|
status: 400,
|
|
838
891
|
headers: { "Content-Type": "application/json" },
|
|
839
892
|
});
|
|
840
893
|
}
|
|
841
894
|
const maxResults = aiConfig.maxResults ?? 5;
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
895
|
+
await emitTrace({
|
|
896
|
+
type: "user.input",
|
|
897
|
+
name: "ask-ai",
|
|
898
|
+
parentSpanId: runSpanId,
|
|
899
|
+
startedAt: new Date().toISOString(),
|
|
900
|
+
endedAt: new Date().toISOString(),
|
|
901
|
+
durationMs: 0,
|
|
902
|
+
status: "success",
|
|
903
|
+
locale: ctx.locale,
|
|
904
|
+
inputPreview: {
|
|
905
|
+
messageCount: messages.length,
|
|
906
|
+
questionLength: lastUserMessage.content.length,
|
|
907
|
+
requestedModel: typeof body.model === "string" && body.model.trim().length > 0
|
|
908
|
+
? body.model.trim()
|
|
909
|
+
: undefined,
|
|
910
|
+
},
|
|
911
|
+
});
|
|
912
|
+
const retrievalStartedAt = Date.now();
|
|
913
|
+
const retrievalStartedAtIso = new Date().toISOString();
|
|
914
|
+
const retrievalSpanId = createDocsAgentTraceId("span");
|
|
915
|
+
await emitTrace({
|
|
916
|
+
type: "retrieval.query",
|
|
917
|
+
name: "docs-index",
|
|
918
|
+
spanId: retrievalSpanId,
|
|
919
|
+
parentSpanId: runSpanId,
|
|
920
|
+
startedAt: retrievalStartedAtIso,
|
|
921
|
+
status: "started",
|
|
922
|
+
locale: ctx.locale,
|
|
923
|
+
inputPreview: {
|
|
924
|
+
queryLength: lastUserMessage.content.length,
|
|
925
|
+
maxResults,
|
|
926
|
+
},
|
|
927
|
+
});
|
|
928
|
+
const retrieval = await buildDocsAskAIContext({
|
|
929
|
+
pages: getSearchIndex(ctx),
|
|
930
|
+
query: lastUserMessage.content,
|
|
931
|
+
search: resolveSearchRequestConfig(config.search, event.request.url),
|
|
932
|
+
locale: ctx.locale,
|
|
933
|
+
pathname: requestUrl.searchParams.get("pathname") ?? undefined,
|
|
934
|
+
siteTitle: llmsTitle,
|
|
935
|
+
baseUrl: requestUrl.origin,
|
|
936
|
+
limit: maxResults,
|
|
937
|
+
});
|
|
938
|
+
const scored = retrieval.results;
|
|
939
|
+
await emitTrace({
|
|
940
|
+
type: "retrieval.result",
|
|
941
|
+
name: "docs-index",
|
|
942
|
+
parentSpanId: retrievalSpanId,
|
|
943
|
+
startedAt: retrievalStartedAtIso,
|
|
944
|
+
endedAt: new Date().toISOString(),
|
|
945
|
+
durationMs: Math.max(0, Date.now() - retrievalStartedAt),
|
|
946
|
+
status: "success",
|
|
947
|
+
locale: ctx.locale,
|
|
948
|
+
outputPreview: {
|
|
949
|
+
resultCount: scored.length,
|
|
950
|
+
urls: scored.slice(0, 5).map((doc) => doc.url),
|
|
951
|
+
},
|
|
952
|
+
metadata: { maxResults },
|
|
953
|
+
});
|
|
954
|
+
const promptStartedAt = Date.now();
|
|
955
|
+
const promptStartedAtIso = new Date().toISOString();
|
|
956
|
+
const promptSpanId = createDocsAgentTraceId("span");
|
|
957
|
+
const context = retrieval.context;
|
|
845
958
|
const systemPrompt = aiConfig.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
|
|
959
|
+
const packageHintsPrompt = formatDocsAskAIPackageHints(retrieval.packageHints, packageName);
|
|
960
|
+
const fullSystemPrompt = [systemPrompt, packageHintsPrompt].filter(Boolean).join("\n\n");
|
|
846
961
|
const systemMessage = {
|
|
847
962
|
role: "system",
|
|
848
963
|
content: context
|
|
849
|
-
? `${
|
|
850
|
-
:
|
|
964
|
+
? `${fullSystemPrompt}\n\n---\n\nDocumentation context:\n\n${context}`
|
|
965
|
+
: fullSystemPrompt,
|
|
851
966
|
};
|
|
852
967
|
const llmMessages = [
|
|
853
968
|
systemMessage,
|
|
854
969
|
...messages.filter((m) => m.role !== "system"),
|
|
855
970
|
];
|
|
971
|
+
await emitTrace({
|
|
972
|
+
type: "prompt.build",
|
|
973
|
+
name: "ask-ai.prompt",
|
|
974
|
+
spanId: promptSpanId,
|
|
975
|
+
parentSpanId: runSpanId,
|
|
976
|
+
startedAt: promptStartedAtIso,
|
|
977
|
+
endedAt: new Date().toISOString(),
|
|
978
|
+
durationMs: Math.max(0, Date.now() - promptStartedAt),
|
|
979
|
+
status: "success",
|
|
980
|
+
locale: ctx.locale,
|
|
981
|
+
inputPreview: {
|
|
982
|
+
messageCount: messages.length,
|
|
983
|
+
retrievedCount: scored.length,
|
|
984
|
+
},
|
|
985
|
+
outputPreview: {
|
|
986
|
+
llmMessageCount: llmMessages.length,
|
|
987
|
+
contextChars: context.length,
|
|
988
|
+
systemMessageChars: systemMessage.content.length,
|
|
989
|
+
},
|
|
990
|
+
});
|
|
856
991
|
const requestedModel = typeof body.model === "string" && body.model.trim().length > 0
|
|
857
992
|
? body.model.trim()
|
|
858
993
|
: undefined;
|
|
@@ -872,16 +1007,96 @@ export function createDocsServer(config = {}) {
|
|
|
872
1007
|
model: resolved.model,
|
|
873
1008
|
},
|
|
874
1009
|
});
|
|
875
|
-
const
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
1010
|
+
const modelStartedAt = Date.now();
|
|
1011
|
+
const modelStartedAtIso = new Date().toISOString();
|
|
1012
|
+
const modelSpanId = createDocsAgentTraceId("span");
|
|
1013
|
+
const providerOrigin = safeUrlOrigin(resolved.baseUrl);
|
|
1014
|
+
await emitTrace({
|
|
1015
|
+
type: "model.call",
|
|
1016
|
+
name: resolved.model,
|
|
1017
|
+
spanId: modelSpanId,
|
|
1018
|
+
parentSpanId: runSpanId,
|
|
1019
|
+
startedAt: modelStartedAtIso,
|
|
1020
|
+
status: "started",
|
|
1021
|
+
locale: ctx.locale,
|
|
1022
|
+
inputPreview: {
|
|
1023
|
+
messageCount: llmMessages.length,
|
|
1024
|
+
stream: true,
|
|
1025
|
+
providerOrigin,
|
|
880
1026
|
},
|
|
881
|
-
|
|
1027
|
+
metadata: { model: resolved.model },
|
|
882
1028
|
});
|
|
1029
|
+
let llmResponse;
|
|
1030
|
+
try {
|
|
1031
|
+
llmResponse = await fetch(`${resolved.baseUrl}/chat/completions`, {
|
|
1032
|
+
method: "POST",
|
|
1033
|
+
headers: {
|
|
1034
|
+
"Content-Type": "application/json",
|
|
1035
|
+
Authorization: `Bearer ${finalKey}`,
|
|
1036
|
+
},
|
|
1037
|
+
body: JSON.stringify({ model: resolved.model, stream: true, messages: llmMessages }),
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
catch (error) {
|
|
1041
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1042
|
+
await emitTrace({
|
|
1043
|
+
type: "model.error",
|
|
1044
|
+
name: resolved.model,
|
|
1045
|
+
parentSpanId: modelSpanId,
|
|
1046
|
+
startedAt: modelStartedAtIso,
|
|
1047
|
+
endedAt: new Date().toISOString(),
|
|
1048
|
+
durationMs: Math.max(0, Date.now() - modelStartedAt),
|
|
1049
|
+
status: "error",
|
|
1050
|
+
locale: ctx.locale,
|
|
1051
|
+
outputPreview: { message },
|
|
1052
|
+
metadata: { model: resolved.model, providerOrigin },
|
|
1053
|
+
});
|
|
1054
|
+
await emitDocsAnalyticsEvent(analytics, {
|
|
1055
|
+
type: "api_ai_error",
|
|
1056
|
+
source: "server",
|
|
1057
|
+
url: event.request.url,
|
|
1058
|
+
path: requestUrl.pathname,
|
|
1059
|
+
locale: ctx.locale,
|
|
1060
|
+
input: { question: lastUserMessage.content },
|
|
1061
|
+
properties: {
|
|
1062
|
+
reason: "llm_fetch_error",
|
|
1063
|
+
messageCount: messages.length,
|
|
1064
|
+
questionLength: lastUserMessage.content.length,
|
|
1065
|
+
retrievedCount: scored.length,
|
|
1066
|
+
model: resolved.model,
|
|
1067
|
+
durationMs: Math.max(0, Date.now() - requestStartedAt),
|
|
1068
|
+
},
|
|
1069
|
+
});
|
|
1070
|
+
await emitRunError("llm_fetch_error", {
|
|
1071
|
+
status: 502,
|
|
1072
|
+
locale: ctx.locale,
|
|
1073
|
+
messageCount: messages.length,
|
|
1074
|
+
questionLength: lastUserMessage.content.length,
|
|
1075
|
+
retrievedCount: scored.length,
|
|
1076
|
+
model: resolved.model,
|
|
1077
|
+
});
|
|
1078
|
+
return new Response(JSON.stringify({ error: "LLM API request failed." }), {
|
|
1079
|
+
status: 502,
|
|
1080
|
+
headers: { "Content-Type": "application/json" },
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
883
1083
|
if (!llmResponse.ok) {
|
|
884
1084
|
const errText = await llmResponse.text().catch(() => "Unknown error");
|
|
1085
|
+
await emitTrace({
|
|
1086
|
+
type: "model.error",
|
|
1087
|
+
name: resolved.model,
|
|
1088
|
+
parentSpanId: modelSpanId,
|
|
1089
|
+
startedAt: modelStartedAtIso,
|
|
1090
|
+
endedAt: new Date().toISOString(),
|
|
1091
|
+
durationMs: Math.max(0, Date.now() - modelStartedAt),
|
|
1092
|
+
status: "error",
|
|
1093
|
+
locale: ctx.locale,
|
|
1094
|
+
outputPreview: {
|
|
1095
|
+
status: llmResponse.status,
|
|
1096
|
+
errorChars: errText.length,
|
|
1097
|
+
},
|
|
1098
|
+
metadata: { model: resolved.model, providerOrigin },
|
|
1099
|
+
});
|
|
885
1100
|
await emitDocsAnalyticsEvent(analytics, {
|
|
886
1101
|
type: "api_ai_error",
|
|
887
1102
|
source: "server",
|
|
@@ -899,6 +1114,15 @@ export function createDocsServer(config = {}) {
|
|
|
899
1114
|
durationMs: Math.max(0, Date.now() - requestStartedAt),
|
|
900
1115
|
},
|
|
901
1116
|
});
|
|
1117
|
+
await emitRunError("llm_error", {
|
|
1118
|
+
status: 502,
|
|
1119
|
+
modelStatus: llmResponse.status,
|
|
1120
|
+
locale: ctx.locale,
|
|
1121
|
+
messageCount: messages.length,
|
|
1122
|
+
questionLength: lastUserMessage.content.length,
|
|
1123
|
+
retrievedCount: scored.length,
|
|
1124
|
+
model: resolved.model,
|
|
1125
|
+
});
|
|
902
1126
|
return new Response(JSON.stringify({ error: `LLM API error (${llmResponse.status}): ${errText}` }), { status: 502, headers: { "Content-Type": "application/json" } });
|
|
903
1127
|
}
|
|
904
1128
|
await emitDocsAnalyticsEvent(analytics, {
|
|
@@ -916,6 +1140,67 @@ export function createDocsServer(config = {}) {
|
|
|
916
1140
|
durationMs: Math.max(0, Date.now() - requestStartedAt),
|
|
917
1141
|
},
|
|
918
1142
|
});
|
|
1143
|
+
const responseEndedAt = new Date().toISOString();
|
|
1144
|
+
const modelDurationMs = Math.max(0, Date.now() - modelStartedAt);
|
|
1145
|
+
await emitTrace({
|
|
1146
|
+
type: "model.response",
|
|
1147
|
+
name: resolved.model,
|
|
1148
|
+
parentSpanId: modelSpanId,
|
|
1149
|
+
startedAt: modelStartedAtIso,
|
|
1150
|
+
endedAt: responseEndedAt,
|
|
1151
|
+
durationMs: modelDurationMs,
|
|
1152
|
+
status: "success",
|
|
1153
|
+
locale: ctx.locale,
|
|
1154
|
+
outputPreview: {
|
|
1155
|
+
status: llmResponse.status,
|
|
1156
|
+
stream: true,
|
|
1157
|
+
contentType: llmResponse.headers.get("content-type") ?? undefined,
|
|
1158
|
+
},
|
|
1159
|
+
metadata: { model: resolved.model, providerOrigin },
|
|
1160
|
+
});
|
|
1161
|
+
await emitTrace({
|
|
1162
|
+
type: "model.stream",
|
|
1163
|
+
name: resolved.model,
|
|
1164
|
+
parentSpanId: modelSpanId,
|
|
1165
|
+
startedAt: modelStartedAtIso,
|
|
1166
|
+
endedAt: responseEndedAt,
|
|
1167
|
+
durationMs: modelDurationMs,
|
|
1168
|
+
status: "success",
|
|
1169
|
+
locale: ctx.locale,
|
|
1170
|
+
outputPreview: { stream: true },
|
|
1171
|
+
metadata: { model: resolved.model },
|
|
1172
|
+
});
|
|
1173
|
+
const runDurationMs = Math.max(0, Date.now() - requestStartedAt);
|
|
1174
|
+
await emitTrace({
|
|
1175
|
+
type: "agent.final",
|
|
1176
|
+
name: "ask-ai",
|
|
1177
|
+
parentSpanId: runSpanId,
|
|
1178
|
+
startedAt: trace.startedAt,
|
|
1179
|
+
endedAt: new Date().toISOString(),
|
|
1180
|
+
durationMs: runDurationMs,
|
|
1181
|
+
status: "success",
|
|
1182
|
+
locale: ctx.locale,
|
|
1183
|
+
outputPreview: {
|
|
1184
|
+
stream: true,
|
|
1185
|
+
retrievedCount: scored.length,
|
|
1186
|
+
},
|
|
1187
|
+
metadata: { model: resolved.model },
|
|
1188
|
+
});
|
|
1189
|
+
await emitTrace({
|
|
1190
|
+
type: "run.end",
|
|
1191
|
+
name: "ask-ai",
|
|
1192
|
+
spanId: runSpanId,
|
|
1193
|
+
startedAt: trace.startedAt,
|
|
1194
|
+
endedAt: new Date().toISOString(),
|
|
1195
|
+
durationMs: runDurationMs,
|
|
1196
|
+
status: "success",
|
|
1197
|
+
locale: ctx.locale,
|
|
1198
|
+
outputPreview: {
|
|
1199
|
+
stream: true,
|
|
1200
|
+
retrievedCount: scored.length,
|
|
1201
|
+
},
|
|
1202
|
+
metadata: { model: resolved.model },
|
|
1203
|
+
});
|
|
919
1204
|
return new Response(llmResponse.body, {
|
|
920
1205
|
headers: {
|
|
921
1206
|
"Content-Type": "text/event-stream",
|
|
@@ -947,6 +1232,7 @@ export function createDocsServer(config = {}) {
|
|
|
947
1232
|
},
|
|
948
1233
|
mcp: config.mcp,
|
|
949
1234
|
analytics,
|
|
1235
|
+
observability,
|
|
950
1236
|
defaultName: mcpSiteTitle,
|
|
951
1237
|
});
|
|
952
1238
|
return { load, GET, POST, MCP };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farming-labs/svelte",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.73",
|
|
4
4
|
"description": "SvelteKit adapter for @farming-labs/docs — content loading and navigation utilities",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"docs",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@types/node": "^22.10.0",
|
|
58
58
|
"typescript": "^5.9.3",
|
|
59
|
-
"@farming-labs/docs": "0.1.
|
|
59
|
+
"@farming-labs/docs": "0.1.73"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"@farming-labs/docs": "*"
|