@raindrop-ai/opencode-plugin 0.1.1 → 0.1.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/dist/index.cjs +261 -38
- package/dist/index.js +261 -45
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -24,7 +24,7 @@ __export(index_exports, {
|
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(index_exports);
|
|
26
26
|
|
|
27
|
-
// ../core/dist/chunk-
|
|
27
|
+
// ../core/dist/chunk-YUIXRQWN.js
|
|
28
28
|
function getCrypto() {
|
|
29
29
|
const c = globalThis.crypto;
|
|
30
30
|
return c;
|
|
@@ -82,6 +82,20 @@ function base64Encode(bytes) {
|
|
|
82
82
|
function generateId() {
|
|
83
83
|
return randomUUID();
|
|
84
84
|
}
|
|
85
|
+
var HANDOFF_ATTRIBUTE_SUFFIXES = {
|
|
86
|
+
mode: "raindrop.handoff.mode",
|
|
87
|
+
childEventId: "raindrop.handoff.childEventId",
|
|
88
|
+
parentEventId: "raindrop.handoff.parentEventId",
|
|
89
|
+
parentSpanId: "raindrop.handoff.parentSpanId",
|
|
90
|
+
name: "raindrop.handoff.name",
|
|
91
|
+
terminal: "raindrop.handoff.terminal",
|
|
92
|
+
agentRole: "raindrop.agent.role"
|
|
93
|
+
};
|
|
94
|
+
var HANDOFF_TERMINAL_PROPERTY_KEY = HANDOFF_ATTRIBUTE_SUFFIXES.terminal;
|
|
95
|
+
var TRACEPARENT_HEADER = "traceparent";
|
|
96
|
+
var BAGGAGE_HEADER = "baggage";
|
|
97
|
+
var HANDOFF_HEADER = "x-raindrop-handoff";
|
|
98
|
+
var UNRESOLVED_CARRIER_WARNING = `[raindrop] resume: headers were supplied but no hand-off carrier was found on them (searched ${HANDOFF_HEADER}, ${TRACEPARENT_HEADER}, ${BAGGAGE_HEADER}). This run reports as an UNLINKED event, and the launcher that dispatched it will stay on "queued" forever because nothing ever references its child. Passing headers means a parent handed off, so no carrier on them is almost certainly a defect: a gateway or proxy stripping ${HANDOFF_HEADER}, a middleware overwriting ${BAGGAGE_HEADER}, a request forwarded without its headers, or a hand-built carrier whose field names do not match. Send every header the launcher's dispatch returned. Logged once per process; pass no headers for a directly-invoked sub-agent to silence it.`;
|
|
85
99
|
function runWithTracingSuppressed(fn) {
|
|
86
100
|
const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
|
|
87
101
|
if (typeof hook !== "function") return fn();
|
|
@@ -800,6 +814,194 @@ var EventShipper = class {
|
|
|
800
814
|
}
|
|
801
815
|
}
|
|
802
816
|
};
|
|
817
|
+
var MODEL_USAGE_ATTRIBUTES = {
|
|
818
|
+
providerName: "gen_ai.provider.name",
|
|
819
|
+
requestModel: "gen_ai.request.model",
|
|
820
|
+
responseModel: "gen_ai.response.model",
|
|
821
|
+
inputTokens: "gen_ai.usage.input_tokens",
|
|
822
|
+
outputTokens: "gen_ai.usage.output_tokens",
|
|
823
|
+
reasoningTokens: "gen_ai.usage.reasoning_tokens",
|
|
824
|
+
cacheReadInputTokens: "gen_ai.usage.cache_read_input_tokens",
|
|
825
|
+
cacheWriteInputTokens: "gen_ai.usage.cache_write_input_tokens",
|
|
826
|
+
nonCachedInputTokens: "raindrop.usage.input_tokens.non_cached",
|
|
827
|
+
nonReasoningOutputTokens: "raindrop.usage.output_tokens.non_reasoning",
|
|
828
|
+
totalOutputTokens: "raindrop.usage.output_tokens.total",
|
|
829
|
+
reasoningOutputTokens: "raindrop.usage.output_tokens.reasoning",
|
|
830
|
+
cacheReadTokens: "raindrop.usage.input_tokens.cache_read",
|
|
831
|
+
cacheWriteTokens: "raindrop.usage.input_tokens.cache_write"
|
|
832
|
+
};
|
|
833
|
+
var MODEL_PROVIDER_NAMES = [
|
|
834
|
+
["google.vertex", "google-vertex"],
|
|
835
|
+
["vertex.anthropic", "google-vertex"],
|
|
836
|
+
["amazon-bedrock", "amazon-bedrock"],
|
|
837
|
+
["bedrock", "amazon-bedrock"],
|
|
838
|
+
["anthropic", "anthropic"],
|
|
839
|
+
["openai", "openai"],
|
|
840
|
+
["google", "google"],
|
|
841
|
+
["azure", "azure"],
|
|
842
|
+
["openrouter", "openrouter"]
|
|
843
|
+
];
|
|
844
|
+
function canonicalModelProvider(provider) {
|
|
845
|
+
var _a;
|
|
846
|
+
const normalizedProvider = provider.trim().toLowerCase();
|
|
847
|
+
if (normalizedProvider === "gateway") return "vercel";
|
|
848
|
+
const match = MODEL_PROVIDER_NAMES.find(
|
|
849
|
+
([family]) => normalizedProvider === family || normalizedProvider.startsWith(`${family}.`)
|
|
850
|
+
);
|
|
851
|
+
return (_a = match == null ? void 0 : match[1]) != null ? _a : provider;
|
|
852
|
+
}
|
|
853
|
+
function tokenCount(value) {
|
|
854
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
|
|
855
|
+
}
|
|
856
|
+
function exclusiveTokens({
|
|
857
|
+
explicit,
|
|
858
|
+
total,
|
|
859
|
+
parts,
|
|
860
|
+
semantics
|
|
861
|
+
}) {
|
|
862
|
+
if (explicit !== void 0) return explicit;
|
|
863
|
+
if (total === void 0) return void 0;
|
|
864
|
+
if (semantics === "exclusive") return total;
|
|
865
|
+
const observedParts = parts.filter((part) => part !== void 0 && part > 0);
|
|
866
|
+
if (semantics === void 0 && observedParts.length > 0) return void 0;
|
|
867
|
+
const exclusive = total - observedParts.reduce((sum, part) => sum + part, 0);
|
|
868
|
+
return exclusive >= 0 ? exclusive : void 0;
|
|
869
|
+
}
|
|
870
|
+
function buildModelUsageAttributes(usage) {
|
|
871
|
+
const inputTokens = tokenCount(usage.inputTokens);
|
|
872
|
+
const outputTokens = tokenCount(usage.outputTokens);
|
|
873
|
+
const explicitNonCachedInputTokens = tokenCount(usage.nonCachedInputTokens);
|
|
874
|
+
const explicitNonReasoningOutputTokens = tokenCount(usage.nonReasoningOutputTokens);
|
|
875
|
+
const reasoningTokens = tokenCount(usage.reasoningTokens);
|
|
876
|
+
const cacheReadInputTokens = tokenCount(usage.cacheReadInputTokens);
|
|
877
|
+
const cacheWriteInputTokens = tokenCount(usage.cacheWriteInputTokens);
|
|
878
|
+
const nonCachedInputTokens = exclusiveTokens({
|
|
879
|
+
explicit: explicitNonCachedInputTokens,
|
|
880
|
+
total: inputTokens,
|
|
881
|
+
parts: [cacheReadInputTokens, cacheWriteInputTokens],
|
|
882
|
+
semantics: usage.inputTokenSemantics
|
|
883
|
+
});
|
|
884
|
+
const nonReasoningOutputTokens = exclusiveTokens({
|
|
885
|
+
explicit: explicitNonReasoningOutputTokens,
|
|
886
|
+
total: outputTokens,
|
|
887
|
+
parts: [reasoningTokens],
|
|
888
|
+
semantics: usage.outputTokenSemantics
|
|
889
|
+
});
|
|
890
|
+
const exclusivePoolAttributes = nonCachedInputTokens !== void 0 && nonReasoningOutputTokens !== void 0 ? [
|
|
891
|
+
// backend consumes this complete raindrop.* set as one atomic billing-pool contract.
|
|
892
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.nonCachedInputTokens, nonCachedInputTokens),
|
|
893
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.nonReasoningOutputTokens, nonReasoningOutputTokens),
|
|
894
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.reasoningOutputTokens, reasoningTokens),
|
|
895
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadTokens, cacheReadInputTokens),
|
|
896
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteTokens, cacheWriteInputTokens)
|
|
897
|
+
] : [];
|
|
898
|
+
return [
|
|
899
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.inputTokens, inputTokens),
|
|
900
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.outputTokens, outputTokens),
|
|
901
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.reasoningTokens, reasoningTokens),
|
|
902
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens, cacheReadInputTokens),
|
|
903
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens, cacheWriteInputTokens),
|
|
904
|
+
...exclusivePoolAttributes
|
|
905
|
+
].filter((attribute) => attribute !== void 0);
|
|
906
|
+
}
|
|
907
|
+
var STRING_ALIASES = {
|
|
908
|
+
[MODEL_USAGE_ATTRIBUTES.requestModel]: ["ai.model.id", "ai.model"]
|
|
909
|
+
};
|
|
910
|
+
var NUMBER_ALIASES = {
|
|
911
|
+
[MODEL_USAGE_ATTRIBUTES.inputTokens]: [
|
|
912
|
+
"gen_ai.usage.prompt_tokens",
|
|
913
|
+
"ai.usage.prompt_tokens",
|
|
914
|
+
"ai.usage.promptTokens",
|
|
915
|
+
"ai.usage.input_tokens",
|
|
916
|
+
"ai.usage.inputTokens"
|
|
917
|
+
],
|
|
918
|
+
[MODEL_USAGE_ATTRIBUTES.outputTokens]: [
|
|
919
|
+
"gen_ai.usage.completion_tokens",
|
|
920
|
+
"ai.usage.completion_tokens",
|
|
921
|
+
"ai.usage.completionTokens",
|
|
922
|
+
"ai.usage.output_tokens",
|
|
923
|
+
"ai.usage.outputTokens"
|
|
924
|
+
],
|
|
925
|
+
[MODEL_USAGE_ATTRIBUTES.reasoningTokens]: [
|
|
926
|
+
"gen_ai.usage.reasoning.output_tokens",
|
|
927
|
+
"ai.usage.reasoningTokens",
|
|
928
|
+
"ai.usage.thoughts_tokens"
|
|
929
|
+
],
|
|
930
|
+
[MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens]: [
|
|
931
|
+
"gen_ai.usage.cache_read_tokens",
|
|
932
|
+
"gen_ai.usage.cache_read.input_tokens",
|
|
933
|
+
"ai.usage.cachedInputTokens",
|
|
934
|
+
"ai.usage.cached_tokens",
|
|
935
|
+
"ai.usage.cache_read_tokens",
|
|
936
|
+
"ai.usage.cache_read_input_tokens",
|
|
937
|
+
"ai.usage.cacheReadInputTokens"
|
|
938
|
+
],
|
|
939
|
+
[MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens]: [
|
|
940
|
+
"gen_ai.usage.cache_creation_input_tokens",
|
|
941
|
+
"gen_ai.usage.cache_creation.input_tokens",
|
|
942
|
+
"ai.usage.cacheWriteInputTokens",
|
|
943
|
+
"ai.usage.cache_creation_input_tokens",
|
|
944
|
+
"ai.usage.cacheCreationInputTokens"
|
|
945
|
+
]
|
|
946
|
+
};
|
|
947
|
+
function hasAttribute(attributes, key) {
|
|
948
|
+
return attributes.some((attribute) => attribute.key === key);
|
|
949
|
+
}
|
|
950
|
+
function findStringAttribute(attributes, keys) {
|
|
951
|
+
return attributes.find(
|
|
952
|
+
(attribute) => keys.includes(attribute.key) && typeof attribute.value.stringValue === "string" && attribute.value.stringValue.length > 0
|
|
953
|
+
);
|
|
954
|
+
}
|
|
955
|
+
function findNumberAttribute(attributes, keys) {
|
|
956
|
+
return attributes.find((attribute) => {
|
|
957
|
+
if (!keys.includes(attribute.key)) return false;
|
|
958
|
+
if (typeof attribute.value.doubleValue === "number" && Number.isSafeInteger(attribute.value.doubleValue) && attribute.value.doubleValue >= 0) {
|
|
959
|
+
return true;
|
|
960
|
+
}
|
|
961
|
+
const intValue = attribute.value.intValue;
|
|
962
|
+
return integerValue(intValue) !== void 0;
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
function integerValue(value) {
|
|
966
|
+
if (typeof value === "number") {
|
|
967
|
+
return Number.isSafeInteger(value) && value >= 0 ? value : void 0;
|
|
968
|
+
}
|
|
969
|
+
if (typeof value !== "string") return void 0;
|
|
970
|
+
const parsed = Number(value);
|
|
971
|
+
return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : void 0;
|
|
972
|
+
}
|
|
973
|
+
function appendAlias(attributes, target, source) {
|
|
974
|
+
if (!source || hasAttribute(attributes, target)) return attributes;
|
|
975
|
+
return [...attributes, { key: target, value: { ...source.value } }];
|
|
976
|
+
}
|
|
977
|
+
function appendProviderAlias(attributes) {
|
|
978
|
+
if (hasAttribute(attributes, MODEL_USAGE_ATTRIBUTES.providerName)) return attributes;
|
|
979
|
+
const source = findStringAttribute(attributes, ["gen_ai.system", "ai.model.provider"]);
|
|
980
|
+
if (!(source == null ? void 0 : source.value.stringValue)) return attributes;
|
|
981
|
+
const attribute = attrString(
|
|
982
|
+
MODEL_USAGE_ATTRIBUTES.providerName,
|
|
983
|
+
canonicalModelProvider(source.value.stringValue)
|
|
984
|
+
);
|
|
985
|
+
return attribute ? [...attributes, attribute] : attributes;
|
|
986
|
+
}
|
|
987
|
+
function normalizeModelUsageSpan(span) {
|
|
988
|
+
const original = span.attributes;
|
|
989
|
+
if (!original || original.length === 0) return span;
|
|
990
|
+
const responseModel = findStringAttribute(original, [
|
|
991
|
+
MODEL_USAGE_ATTRIBUTES.responseModel,
|
|
992
|
+
"ai.response.model"
|
|
993
|
+
]);
|
|
994
|
+
if (!responseModel) return span;
|
|
995
|
+
let attributes = appendAlias(original, MODEL_USAGE_ATTRIBUTES.responseModel, responseModel);
|
|
996
|
+
attributes = appendProviderAlias(attributes);
|
|
997
|
+
for (const [target, aliases] of Object.entries(STRING_ALIASES)) {
|
|
998
|
+
attributes = appendAlias(attributes, target, findStringAttribute(attributes, aliases));
|
|
999
|
+
}
|
|
1000
|
+
for (const [target, aliases] of Object.entries(NUMBER_ALIASES)) {
|
|
1001
|
+
attributes = appendAlias(attributes, target, findNumberAttribute(attributes, aliases));
|
|
1002
|
+
}
|
|
1003
|
+
return attributes === original ? span : { ...span, attributes };
|
|
1004
|
+
}
|
|
803
1005
|
var DEFAULT_SECRET_KEY_NAMES = [
|
|
804
1006
|
"apikey",
|
|
805
1007
|
"apisecret",
|
|
@@ -1000,6 +1202,10 @@ var TraceShipper = class {
|
|
|
1000
1202
|
return null;
|
|
1001
1203
|
}
|
|
1002
1204
|
}
|
|
1205
|
+
try {
|
|
1206
|
+
current = normalizeModelUsageSpan(current);
|
|
1207
|
+
} catch (e) {
|
|
1208
|
+
}
|
|
1003
1209
|
if (!this.disableDefaultRedaction) {
|
|
1004
1210
|
current = defaultTransformSpan(current);
|
|
1005
1211
|
}
|
|
@@ -1296,7 +1502,7 @@ function resolveLocalWorkshopUrl(fileValue) {
|
|
|
1296
1502
|
// package.json
|
|
1297
1503
|
var package_default = {
|
|
1298
1504
|
name: "@raindrop-ai/opencode-plugin",
|
|
1299
|
-
version: "0.1.
|
|
1505
|
+
version: "0.1.2",
|
|
1300
1506
|
description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
|
|
1301
1507
|
type: "module",
|
|
1302
1508
|
main: "dist/index.js",
|
|
@@ -1401,6 +1607,9 @@ var TraceShipper2 = class extends TraceShipper {
|
|
|
1401
1607
|
}
|
|
1402
1608
|
};
|
|
1403
1609
|
|
|
1610
|
+
// src/tracing.ts
|
|
1611
|
+
var import_node_os2 = require("os");
|
|
1612
|
+
|
|
1404
1613
|
// src/bounded.ts
|
|
1405
1614
|
var TRUNCATION_MARKER2 = "...[truncated by raindrop]";
|
|
1406
1615
|
var MAX_TEXT_FIELD_CHARS = 1e6;
|
|
@@ -1481,6 +1690,49 @@ function boundedStringify(value, limit = MAX_TEXT_FIELD_CHARS) {
|
|
|
1481
1690
|
}
|
|
1482
1691
|
}
|
|
1483
1692
|
|
|
1693
|
+
// src/model-spend.ts
|
|
1694
|
+
function parseOpenCodeAssistantTokens(tokens) {
|
|
1695
|
+
const input = tokens["input"];
|
|
1696
|
+
const output = tokens["output"];
|
|
1697
|
+
if (typeof input !== "number" || typeof output !== "number") return void 0;
|
|
1698
|
+
const reasoning = tokens["reasoning"];
|
|
1699
|
+
const cache = tokens["cache"];
|
|
1700
|
+
const cacheObj = cache && typeof cache === "object" && cache !== null ? cache : void 0;
|
|
1701
|
+
return {
|
|
1702
|
+
input,
|
|
1703
|
+
output,
|
|
1704
|
+
reasoning: typeof reasoning === "number" ? reasoning : 0,
|
|
1705
|
+
cacheRead: typeof (cacheObj == null ? void 0 : cacheObj["read"]) === "number" ? cacheObj["read"] : 0,
|
|
1706
|
+
cacheWrite: typeof (cacheObj == null ? void 0 : cacheObj["write"]) === "number" ? cacheObj["write"] : 0
|
|
1707
|
+
};
|
|
1708
|
+
}
|
|
1709
|
+
function modelSpendSpanAttributes(input) {
|
|
1710
|
+
const providerAttr = attrString(
|
|
1711
|
+
"gen_ai.provider.name",
|
|
1712
|
+
canonicalModelProvider(input.providerID)
|
|
1713
|
+
);
|
|
1714
|
+
const usageAttrs = buildModelUsageAttributes({
|
|
1715
|
+
inputTokens: input.tokens.input,
|
|
1716
|
+
outputTokens: input.tokens.output,
|
|
1717
|
+
reasoningTokens: input.tokens.reasoning,
|
|
1718
|
+
cacheReadInputTokens: input.tokens.cacheRead,
|
|
1719
|
+
cacheWriteInputTokens: input.tokens.cacheWrite,
|
|
1720
|
+
inputTokenSemantics: "exclusive",
|
|
1721
|
+
outputTokenSemantics: "exclusive"
|
|
1722
|
+
});
|
|
1723
|
+
return providerAttr ? [providerAttr, ...usageAttrs] : usageAttrs;
|
|
1724
|
+
}
|
|
1725
|
+
function buildLlmGenAiAttributes(input) {
|
|
1726
|
+
const parsed = parseOpenCodeAssistantTokens(input.tokens);
|
|
1727
|
+
if (!parsed) return [];
|
|
1728
|
+
return [
|
|
1729
|
+
attrString("gen_ai.system", input.providerID),
|
|
1730
|
+
attrString("gen_ai.request.model", input.modelID),
|
|
1731
|
+
attrString("gen_ai.response.model", input.modelID),
|
|
1732
|
+
...modelSpendSpanAttributes({ providerID: input.providerID, tokens: parsed })
|
|
1733
|
+
].filter((attribute) => attribute !== void 0);
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1484
1736
|
// src/tracing-linkage-helpers.ts
|
|
1485
1737
|
function createSessionParentMapHelpers({
|
|
1486
1738
|
sessions: sessions2,
|
|
@@ -1645,7 +1897,7 @@ function getHostname() {
|
|
|
1645
1897
|
try {
|
|
1646
1898
|
const bunGlobal = globalThis;
|
|
1647
1899
|
if ((_a = bunGlobal.Bun) == null ? void 0 : _a.hostname) return bunGlobal.Bun.hostname;
|
|
1648
|
-
return
|
|
1900
|
+
return (0, import_node_os2.hostname)();
|
|
1649
1901
|
} catch (e) {
|
|
1650
1902
|
const h = process.env["HOSTNAME"];
|
|
1651
1903
|
if (h === void 0 || h === "") throw new Error("HOSTNAME env is not set");
|
|
@@ -1794,7 +2046,6 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
1794
2046
|
const tokensObj = tokens;
|
|
1795
2047
|
const inputTokens = tokensObj["input"];
|
|
1796
2048
|
const outputTokens = tokensObj["output"];
|
|
1797
|
-
const reasoningTokens = tokensObj["reasoning"];
|
|
1798
2049
|
if (typeof inputTokens !== "number" || typeof outputTokens !== "number")
|
|
1799
2050
|
throw new Error("message.updated: tokens.input and tokens.output must be numbers");
|
|
1800
2051
|
const providerID = info2["providerID"];
|
|
@@ -1812,13 +2063,7 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
1812
2063
|
state.turnOutputParts.push(outputText);
|
|
1813
2064
|
}
|
|
1814
2065
|
state.turnOutputTokens += outputTokens;
|
|
1815
|
-
|
|
1816
|
-
if (outputText === void 0 && !(isToolCallsCompletion && hasIntermediateReasoning))
|
|
1817
|
-
return;
|
|
1818
|
-
if (isToolCallsCompletion && !hasIntermediateReasoning) {
|
|
1819
|
-
await traceShipper.flush();
|
|
1820
|
-
return;
|
|
1821
|
-
}
|
|
2066
|
+
if (outputText === void 0 && !isToolCallsCompletion) return;
|
|
1822
2067
|
const msgError = info2["error"];
|
|
1823
2068
|
const errorForSpan = msgError ? (() => {
|
|
1824
2069
|
var _a2, _b2;
|
|
@@ -1831,20 +2076,12 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
1831
2076
|
type: ${name != null ? name : "UnknownError"}`;
|
|
1832
2077
|
})() : void 0;
|
|
1833
2078
|
if (isToolCallsCompletion) {
|
|
1834
|
-
|
|
1835
|
-
throw new Error("message.updated: reasoningParts missing for tool-call continuation");
|
|
2079
|
+
const responseText = reasoningText !== void 0 && reasoningText.trim().length > 0 ? reasoningText.trim() : outputText !== void 0 && outputText.trim().length > 0 ? outputText.trim() : "";
|
|
1836
2080
|
const llmAttrs = [
|
|
1837
2081
|
attrString("ai.operationId", "generateText"),
|
|
1838
|
-
attrString("ai.response.text",
|
|
1839
|
-
|
|
1840
|
-
attrString("gen_ai.request.model", modelID),
|
|
1841
|
-
attrString("gen_ai.response.model", modelID),
|
|
1842
|
-
attrInt("gen_ai.usage.input_tokens", inputTokens),
|
|
1843
|
-
attrInt("gen_ai.usage.output_tokens", outputTokens)
|
|
2082
|
+
attrString("ai.response.text", responseText),
|
|
2083
|
+
...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
|
|
1844
2084
|
];
|
|
1845
|
-
if (reasoningTokens != null && typeof reasoningTokens === "number" && reasoningTokens > 0) {
|
|
1846
|
-
llmAttrs.push(attrInt("gen_ai.usage.reasoning_tokens", reasoningTokens));
|
|
1847
|
-
}
|
|
1848
2085
|
if (state.currentSystemPrompt) {
|
|
1849
2086
|
llmAttrs.push(
|
|
1850
2087
|
attrString("gen_ai.prompt.0.role", "system"),
|
|
@@ -1879,15 +2116,8 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1879
2116
|
buildPromptMessages(state.currentSystemPrompt, state.currentInput)
|
|
1880
2117
|
),
|
|
1881
2118
|
attrString("ai.response.text", turnOutputText),
|
|
1882
|
-
|
|
1883
|
-
attrString("gen_ai.request.model", modelID),
|
|
1884
|
-
attrString("gen_ai.response.model", modelID),
|
|
1885
|
-
attrInt("gen_ai.usage.input_tokens", inputTokens),
|
|
1886
|
-
attrInt("gen_ai.usage.output_tokens", outputTokens)
|
|
2119
|
+
...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
|
|
1887
2120
|
];
|
|
1888
|
-
if (reasoningTokens != null && typeof reasoningTokens === "number" && reasoningTokens > 0) {
|
|
1889
|
-
llmAttrs.push(attrInt("gen_ai.usage.reasoning_tokens", reasoningTokens));
|
|
1890
|
-
}
|
|
1891
2121
|
if (state.currentSystemPrompt) {
|
|
1892
2122
|
llmAttrs.push(
|
|
1893
2123
|
attrString("gen_ai.prompt.0.role", "system"),
|
|
@@ -1931,19 +2161,12 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1931
2161
|
buildPromptMessages(state.currentSystemPrompt, state.currentInput)
|
|
1932
2162
|
),
|
|
1933
2163
|
attrString("ai.response.text", turnOutputText),
|
|
1934
|
-
|
|
1935
|
-
attrString("gen_ai.request.model", modelID),
|
|
1936
|
-
attrString("gen_ai.response.model", modelID),
|
|
1937
|
-
attrInt("gen_ai.usage.input_tokens", inputTokens),
|
|
1938
|
-
attrInt("gen_ai.usage.output_tokens", outputTokens)
|
|
2164
|
+
...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
|
|
1939
2165
|
];
|
|
1940
2166
|
if (isCompaction) {
|
|
1941
2167
|
rootAttrs.push(attrString("is_compaction", "true"));
|
|
1942
2168
|
}
|
|
1943
2169
|
rootAttrs.push(attrString("message_id", messageId));
|
|
1944
|
-
if (reasoningTokens != null && typeof reasoningTokens === "number" && reasoningTokens > 0) {
|
|
1945
|
-
rootAttrs.push(attrInt("gen_ai.usage.reasoning_tokens", reasoningTokens));
|
|
1946
|
-
}
|
|
1947
2170
|
if (state.currentSystemPrompt) {
|
|
1948
2171
|
rootAttrs.push(
|
|
1949
2172
|
attrString("gen_ai.prompt.0.role", "system"),
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
// ../core/dist/chunk-QTUH3BHZ.js
|
|
1
|
+
// ../core/dist/chunk-YUIXRQWN.js
|
|
9
2
|
function getCrypto() {
|
|
10
3
|
const c = globalThis.crypto;
|
|
11
4
|
return c;
|
|
@@ -63,6 +56,20 @@ function base64Encode(bytes) {
|
|
|
63
56
|
function generateId() {
|
|
64
57
|
return randomUUID();
|
|
65
58
|
}
|
|
59
|
+
var HANDOFF_ATTRIBUTE_SUFFIXES = {
|
|
60
|
+
mode: "raindrop.handoff.mode",
|
|
61
|
+
childEventId: "raindrop.handoff.childEventId",
|
|
62
|
+
parentEventId: "raindrop.handoff.parentEventId",
|
|
63
|
+
parentSpanId: "raindrop.handoff.parentSpanId",
|
|
64
|
+
name: "raindrop.handoff.name",
|
|
65
|
+
terminal: "raindrop.handoff.terminal",
|
|
66
|
+
agentRole: "raindrop.agent.role"
|
|
67
|
+
};
|
|
68
|
+
var HANDOFF_TERMINAL_PROPERTY_KEY = HANDOFF_ATTRIBUTE_SUFFIXES.terminal;
|
|
69
|
+
var TRACEPARENT_HEADER = "traceparent";
|
|
70
|
+
var BAGGAGE_HEADER = "baggage";
|
|
71
|
+
var HANDOFF_HEADER = "x-raindrop-handoff";
|
|
72
|
+
var UNRESOLVED_CARRIER_WARNING = `[raindrop] resume: headers were supplied but no hand-off carrier was found on them (searched ${HANDOFF_HEADER}, ${TRACEPARENT_HEADER}, ${BAGGAGE_HEADER}). This run reports as an UNLINKED event, and the launcher that dispatched it will stay on "queued" forever because nothing ever references its child. Passing headers means a parent handed off, so no carrier on them is almost certainly a defect: a gateway or proxy stripping ${HANDOFF_HEADER}, a middleware overwriting ${BAGGAGE_HEADER}, a request forwarded without its headers, or a hand-built carrier whose field names do not match. Send every header the launcher's dispatch returned. Logged once per process; pass no headers for a directly-invoked sub-agent to silence it.`;
|
|
66
73
|
function runWithTracingSuppressed(fn) {
|
|
67
74
|
const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
|
|
68
75
|
if (typeof hook !== "function") return fn();
|
|
@@ -781,6 +788,194 @@ var EventShipper = class {
|
|
|
781
788
|
}
|
|
782
789
|
}
|
|
783
790
|
};
|
|
791
|
+
var MODEL_USAGE_ATTRIBUTES = {
|
|
792
|
+
providerName: "gen_ai.provider.name",
|
|
793
|
+
requestModel: "gen_ai.request.model",
|
|
794
|
+
responseModel: "gen_ai.response.model",
|
|
795
|
+
inputTokens: "gen_ai.usage.input_tokens",
|
|
796
|
+
outputTokens: "gen_ai.usage.output_tokens",
|
|
797
|
+
reasoningTokens: "gen_ai.usage.reasoning_tokens",
|
|
798
|
+
cacheReadInputTokens: "gen_ai.usage.cache_read_input_tokens",
|
|
799
|
+
cacheWriteInputTokens: "gen_ai.usage.cache_write_input_tokens",
|
|
800
|
+
nonCachedInputTokens: "raindrop.usage.input_tokens.non_cached",
|
|
801
|
+
nonReasoningOutputTokens: "raindrop.usage.output_tokens.non_reasoning",
|
|
802
|
+
totalOutputTokens: "raindrop.usage.output_tokens.total",
|
|
803
|
+
reasoningOutputTokens: "raindrop.usage.output_tokens.reasoning",
|
|
804
|
+
cacheReadTokens: "raindrop.usage.input_tokens.cache_read",
|
|
805
|
+
cacheWriteTokens: "raindrop.usage.input_tokens.cache_write"
|
|
806
|
+
};
|
|
807
|
+
var MODEL_PROVIDER_NAMES = [
|
|
808
|
+
["google.vertex", "google-vertex"],
|
|
809
|
+
["vertex.anthropic", "google-vertex"],
|
|
810
|
+
["amazon-bedrock", "amazon-bedrock"],
|
|
811
|
+
["bedrock", "amazon-bedrock"],
|
|
812
|
+
["anthropic", "anthropic"],
|
|
813
|
+
["openai", "openai"],
|
|
814
|
+
["google", "google"],
|
|
815
|
+
["azure", "azure"],
|
|
816
|
+
["openrouter", "openrouter"]
|
|
817
|
+
];
|
|
818
|
+
function canonicalModelProvider(provider) {
|
|
819
|
+
var _a;
|
|
820
|
+
const normalizedProvider = provider.trim().toLowerCase();
|
|
821
|
+
if (normalizedProvider === "gateway") return "vercel";
|
|
822
|
+
const match = MODEL_PROVIDER_NAMES.find(
|
|
823
|
+
([family]) => normalizedProvider === family || normalizedProvider.startsWith(`${family}.`)
|
|
824
|
+
);
|
|
825
|
+
return (_a = match == null ? void 0 : match[1]) != null ? _a : provider;
|
|
826
|
+
}
|
|
827
|
+
function tokenCount(value) {
|
|
828
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
|
|
829
|
+
}
|
|
830
|
+
function exclusiveTokens({
|
|
831
|
+
explicit,
|
|
832
|
+
total,
|
|
833
|
+
parts,
|
|
834
|
+
semantics
|
|
835
|
+
}) {
|
|
836
|
+
if (explicit !== void 0) return explicit;
|
|
837
|
+
if (total === void 0) return void 0;
|
|
838
|
+
if (semantics === "exclusive") return total;
|
|
839
|
+
const observedParts = parts.filter((part) => part !== void 0 && part > 0);
|
|
840
|
+
if (semantics === void 0 && observedParts.length > 0) return void 0;
|
|
841
|
+
const exclusive = total - observedParts.reduce((sum, part) => sum + part, 0);
|
|
842
|
+
return exclusive >= 0 ? exclusive : void 0;
|
|
843
|
+
}
|
|
844
|
+
function buildModelUsageAttributes(usage) {
|
|
845
|
+
const inputTokens = tokenCount(usage.inputTokens);
|
|
846
|
+
const outputTokens = tokenCount(usage.outputTokens);
|
|
847
|
+
const explicitNonCachedInputTokens = tokenCount(usage.nonCachedInputTokens);
|
|
848
|
+
const explicitNonReasoningOutputTokens = tokenCount(usage.nonReasoningOutputTokens);
|
|
849
|
+
const reasoningTokens = tokenCount(usage.reasoningTokens);
|
|
850
|
+
const cacheReadInputTokens = tokenCount(usage.cacheReadInputTokens);
|
|
851
|
+
const cacheWriteInputTokens = tokenCount(usage.cacheWriteInputTokens);
|
|
852
|
+
const nonCachedInputTokens = exclusiveTokens({
|
|
853
|
+
explicit: explicitNonCachedInputTokens,
|
|
854
|
+
total: inputTokens,
|
|
855
|
+
parts: [cacheReadInputTokens, cacheWriteInputTokens],
|
|
856
|
+
semantics: usage.inputTokenSemantics
|
|
857
|
+
});
|
|
858
|
+
const nonReasoningOutputTokens = exclusiveTokens({
|
|
859
|
+
explicit: explicitNonReasoningOutputTokens,
|
|
860
|
+
total: outputTokens,
|
|
861
|
+
parts: [reasoningTokens],
|
|
862
|
+
semantics: usage.outputTokenSemantics
|
|
863
|
+
});
|
|
864
|
+
const exclusivePoolAttributes = nonCachedInputTokens !== void 0 && nonReasoningOutputTokens !== void 0 ? [
|
|
865
|
+
// backend consumes this complete raindrop.* set as one atomic billing-pool contract.
|
|
866
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.nonCachedInputTokens, nonCachedInputTokens),
|
|
867
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.nonReasoningOutputTokens, nonReasoningOutputTokens),
|
|
868
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.reasoningOutputTokens, reasoningTokens),
|
|
869
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadTokens, cacheReadInputTokens),
|
|
870
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteTokens, cacheWriteInputTokens)
|
|
871
|
+
] : [];
|
|
872
|
+
return [
|
|
873
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.inputTokens, inputTokens),
|
|
874
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.outputTokens, outputTokens),
|
|
875
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.reasoningTokens, reasoningTokens),
|
|
876
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens, cacheReadInputTokens),
|
|
877
|
+
attrInt(MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens, cacheWriteInputTokens),
|
|
878
|
+
...exclusivePoolAttributes
|
|
879
|
+
].filter((attribute) => attribute !== void 0);
|
|
880
|
+
}
|
|
881
|
+
var STRING_ALIASES = {
|
|
882
|
+
[MODEL_USAGE_ATTRIBUTES.requestModel]: ["ai.model.id", "ai.model"]
|
|
883
|
+
};
|
|
884
|
+
var NUMBER_ALIASES = {
|
|
885
|
+
[MODEL_USAGE_ATTRIBUTES.inputTokens]: [
|
|
886
|
+
"gen_ai.usage.prompt_tokens",
|
|
887
|
+
"ai.usage.prompt_tokens",
|
|
888
|
+
"ai.usage.promptTokens",
|
|
889
|
+
"ai.usage.input_tokens",
|
|
890
|
+
"ai.usage.inputTokens"
|
|
891
|
+
],
|
|
892
|
+
[MODEL_USAGE_ATTRIBUTES.outputTokens]: [
|
|
893
|
+
"gen_ai.usage.completion_tokens",
|
|
894
|
+
"ai.usage.completion_tokens",
|
|
895
|
+
"ai.usage.completionTokens",
|
|
896
|
+
"ai.usage.output_tokens",
|
|
897
|
+
"ai.usage.outputTokens"
|
|
898
|
+
],
|
|
899
|
+
[MODEL_USAGE_ATTRIBUTES.reasoningTokens]: [
|
|
900
|
+
"gen_ai.usage.reasoning.output_tokens",
|
|
901
|
+
"ai.usage.reasoningTokens",
|
|
902
|
+
"ai.usage.thoughts_tokens"
|
|
903
|
+
],
|
|
904
|
+
[MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens]: [
|
|
905
|
+
"gen_ai.usage.cache_read_tokens",
|
|
906
|
+
"gen_ai.usage.cache_read.input_tokens",
|
|
907
|
+
"ai.usage.cachedInputTokens",
|
|
908
|
+
"ai.usage.cached_tokens",
|
|
909
|
+
"ai.usage.cache_read_tokens",
|
|
910
|
+
"ai.usage.cache_read_input_tokens",
|
|
911
|
+
"ai.usage.cacheReadInputTokens"
|
|
912
|
+
],
|
|
913
|
+
[MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens]: [
|
|
914
|
+
"gen_ai.usage.cache_creation_input_tokens",
|
|
915
|
+
"gen_ai.usage.cache_creation.input_tokens",
|
|
916
|
+
"ai.usage.cacheWriteInputTokens",
|
|
917
|
+
"ai.usage.cache_creation_input_tokens",
|
|
918
|
+
"ai.usage.cacheCreationInputTokens"
|
|
919
|
+
]
|
|
920
|
+
};
|
|
921
|
+
function hasAttribute(attributes, key) {
|
|
922
|
+
return attributes.some((attribute) => attribute.key === key);
|
|
923
|
+
}
|
|
924
|
+
function findStringAttribute(attributes, keys) {
|
|
925
|
+
return attributes.find(
|
|
926
|
+
(attribute) => keys.includes(attribute.key) && typeof attribute.value.stringValue === "string" && attribute.value.stringValue.length > 0
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
function findNumberAttribute(attributes, keys) {
|
|
930
|
+
return attributes.find((attribute) => {
|
|
931
|
+
if (!keys.includes(attribute.key)) return false;
|
|
932
|
+
if (typeof attribute.value.doubleValue === "number" && Number.isSafeInteger(attribute.value.doubleValue) && attribute.value.doubleValue >= 0) {
|
|
933
|
+
return true;
|
|
934
|
+
}
|
|
935
|
+
const intValue = attribute.value.intValue;
|
|
936
|
+
return integerValue(intValue) !== void 0;
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
function integerValue(value) {
|
|
940
|
+
if (typeof value === "number") {
|
|
941
|
+
return Number.isSafeInteger(value) && value >= 0 ? value : void 0;
|
|
942
|
+
}
|
|
943
|
+
if (typeof value !== "string") return void 0;
|
|
944
|
+
const parsed = Number(value);
|
|
945
|
+
return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : void 0;
|
|
946
|
+
}
|
|
947
|
+
function appendAlias(attributes, target, source) {
|
|
948
|
+
if (!source || hasAttribute(attributes, target)) return attributes;
|
|
949
|
+
return [...attributes, { key: target, value: { ...source.value } }];
|
|
950
|
+
}
|
|
951
|
+
function appendProviderAlias(attributes) {
|
|
952
|
+
if (hasAttribute(attributes, MODEL_USAGE_ATTRIBUTES.providerName)) return attributes;
|
|
953
|
+
const source = findStringAttribute(attributes, ["gen_ai.system", "ai.model.provider"]);
|
|
954
|
+
if (!(source == null ? void 0 : source.value.stringValue)) return attributes;
|
|
955
|
+
const attribute = attrString(
|
|
956
|
+
MODEL_USAGE_ATTRIBUTES.providerName,
|
|
957
|
+
canonicalModelProvider(source.value.stringValue)
|
|
958
|
+
);
|
|
959
|
+
return attribute ? [...attributes, attribute] : attributes;
|
|
960
|
+
}
|
|
961
|
+
function normalizeModelUsageSpan(span) {
|
|
962
|
+
const original = span.attributes;
|
|
963
|
+
if (!original || original.length === 0) return span;
|
|
964
|
+
const responseModel = findStringAttribute(original, [
|
|
965
|
+
MODEL_USAGE_ATTRIBUTES.responseModel,
|
|
966
|
+
"ai.response.model"
|
|
967
|
+
]);
|
|
968
|
+
if (!responseModel) return span;
|
|
969
|
+
let attributes = appendAlias(original, MODEL_USAGE_ATTRIBUTES.responseModel, responseModel);
|
|
970
|
+
attributes = appendProviderAlias(attributes);
|
|
971
|
+
for (const [target, aliases] of Object.entries(STRING_ALIASES)) {
|
|
972
|
+
attributes = appendAlias(attributes, target, findStringAttribute(attributes, aliases));
|
|
973
|
+
}
|
|
974
|
+
for (const [target, aliases] of Object.entries(NUMBER_ALIASES)) {
|
|
975
|
+
attributes = appendAlias(attributes, target, findNumberAttribute(attributes, aliases));
|
|
976
|
+
}
|
|
977
|
+
return attributes === original ? span : { ...span, attributes };
|
|
978
|
+
}
|
|
784
979
|
var DEFAULT_SECRET_KEY_NAMES = [
|
|
785
980
|
"apikey",
|
|
786
981
|
"apisecret",
|
|
@@ -981,6 +1176,10 @@ var TraceShipper = class {
|
|
|
981
1176
|
return null;
|
|
982
1177
|
}
|
|
983
1178
|
}
|
|
1179
|
+
try {
|
|
1180
|
+
current = normalizeModelUsageSpan(current);
|
|
1181
|
+
} catch (e) {
|
|
1182
|
+
}
|
|
984
1183
|
if (!this.disableDefaultRedaction) {
|
|
985
1184
|
current = defaultTransformSpan(current);
|
|
986
1185
|
}
|
|
@@ -1277,7 +1476,7 @@ function resolveLocalWorkshopUrl(fileValue) {
|
|
|
1277
1476
|
// package.json
|
|
1278
1477
|
var package_default = {
|
|
1279
1478
|
name: "@raindrop-ai/opencode-plugin",
|
|
1280
|
-
version: "0.1.
|
|
1479
|
+
version: "0.1.2",
|
|
1281
1480
|
description: "Raindrop observability plugin for OpenCode \u2014 automatic session/event/span tracing",
|
|
1282
1481
|
type: "module",
|
|
1283
1482
|
main: "dist/index.js",
|
|
@@ -1382,6 +1581,9 @@ var TraceShipper2 = class extends TraceShipper {
|
|
|
1382
1581
|
}
|
|
1383
1582
|
};
|
|
1384
1583
|
|
|
1584
|
+
// src/tracing.ts
|
|
1585
|
+
import { hostname as osHostname } from "os";
|
|
1586
|
+
|
|
1385
1587
|
// src/bounded.ts
|
|
1386
1588
|
var TRUNCATION_MARKER2 = "...[truncated by raindrop]";
|
|
1387
1589
|
var MAX_TEXT_FIELD_CHARS = 1e6;
|
|
@@ -1462,6 +1664,49 @@ function boundedStringify(value, limit = MAX_TEXT_FIELD_CHARS) {
|
|
|
1462
1664
|
}
|
|
1463
1665
|
}
|
|
1464
1666
|
|
|
1667
|
+
// src/model-spend.ts
|
|
1668
|
+
function parseOpenCodeAssistantTokens(tokens) {
|
|
1669
|
+
const input = tokens["input"];
|
|
1670
|
+
const output = tokens["output"];
|
|
1671
|
+
if (typeof input !== "number" || typeof output !== "number") return void 0;
|
|
1672
|
+
const reasoning = tokens["reasoning"];
|
|
1673
|
+
const cache = tokens["cache"];
|
|
1674
|
+
const cacheObj = cache && typeof cache === "object" && cache !== null ? cache : void 0;
|
|
1675
|
+
return {
|
|
1676
|
+
input,
|
|
1677
|
+
output,
|
|
1678
|
+
reasoning: typeof reasoning === "number" ? reasoning : 0,
|
|
1679
|
+
cacheRead: typeof (cacheObj == null ? void 0 : cacheObj["read"]) === "number" ? cacheObj["read"] : 0,
|
|
1680
|
+
cacheWrite: typeof (cacheObj == null ? void 0 : cacheObj["write"]) === "number" ? cacheObj["write"] : 0
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1683
|
+
function modelSpendSpanAttributes(input) {
|
|
1684
|
+
const providerAttr = attrString(
|
|
1685
|
+
"gen_ai.provider.name",
|
|
1686
|
+
canonicalModelProvider(input.providerID)
|
|
1687
|
+
);
|
|
1688
|
+
const usageAttrs = buildModelUsageAttributes({
|
|
1689
|
+
inputTokens: input.tokens.input,
|
|
1690
|
+
outputTokens: input.tokens.output,
|
|
1691
|
+
reasoningTokens: input.tokens.reasoning,
|
|
1692
|
+
cacheReadInputTokens: input.tokens.cacheRead,
|
|
1693
|
+
cacheWriteInputTokens: input.tokens.cacheWrite,
|
|
1694
|
+
inputTokenSemantics: "exclusive",
|
|
1695
|
+
outputTokenSemantics: "exclusive"
|
|
1696
|
+
});
|
|
1697
|
+
return providerAttr ? [providerAttr, ...usageAttrs] : usageAttrs;
|
|
1698
|
+
}
|
|
1699
|
+
function buildLlmGenAiAttributes(input) {
|
|
1700
|
+
const parsed = parseOpenCodeAssistantTokens(input.tokens);
|
|
1701
|
+
if (!parsed) return [];
|
|
1702
|
+
return [
|
|
1703
|
+
attrString("gen_ai.system", input.providerID),
|
|
1704
|
+
attrString("gen_ai.request.model", input.modelID),
|
|
1705
|
+
attrString("gen_ai.response.model", input.modelID),
|
|
1706
|
+
...modelSpendSpanAttributes({ providerID: input.providerID, tokens: parsed })
|
|
1707
|
+
].filter((attribute) => attribute !== void 0);
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1465
1710
|
// src/tracing-linkage-helpers.ts
|
|
1466
1711
|
function createSessionParentMapHelpers({
|
|
1467
1712
|
sessions: sessions2,
|
|
@@ -1626,7 +1871,7 @@ function getHostname() {
|
|
|
1626
1871
|
try {
|
|
1627
1872
|
const bunGlobal = globalThis;
|
|
1628
1873
|
if ((_a = bunGlobal.Bun) == null ? void 0 : _a.hostname) return bunGlobal.Bun.hostname;
|
|
1629
|
-
return
|
|
1874
|
+
return osHostname();
|
|
1630
1875
|
} catch (e) {
|
|
1631
1876
|
const h = process.env["HOSTNAME"];
|
|
1632
1877
|
if (h === void 0 || h === "") throw new Error("HOSTNAME env is not set");
|
|
@@ -1775,7 +2020,6 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
1775
2020
|
const tokensObj = tokens;
|
|
1776
2021
|
const inputTokens = tokensObj["input"];
|
|
1777
2022
|
const outputTokens = tokensObj["output"];
|
|
1778
|
-
const reasoningTokens = tokensObj["reasoning"];
|
|
1779
2023
|
if (typeof inputTokens !== "number" || typeof outputTokens !== "number")
|
|
1780
2024
|
throw new Error("message.updated: tokens.input and tokens.output must be numbers");
|
|
1781
2025
|
const providerID = info2["providerID"];
|
|
@@ -1793,13 +2037,7 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
1793
2037
|
state.turnOutputParts.push(outputText);
|
|
1794
2038
|
}
|
|
1795
2039
|
state.turnOutputTokens += outputTokens;
|
|
1796
|
-
|
|
1797
|
-
if (outputText === void 0 && !(isToolCallsCompletion && hasIntermediateReasoning))
|
|
1798
|
-
return;
|
|
1799
|
-
if (isToolCallsCompletion && !hasIntermediateReasoning) {
|
|
1800
|
-
await traceShipper.flush();
|
|
1801
|
-
return;
|
|
1802
|
-
}
|
|
2040
|
+
if (outputText === void 0 && !isToolCallsCompletion) return;
|
|
1803
2041
|
const msgError = info2["error"];
|
|
1804
2042
|
const errorForSpan = msgError ? (() => {
|
|
1805
2043
|
var _a2, _b2;
|
|
@@ -1812,20 +2050,12 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
|
|
|
1812
2050
|
type: ${name != null ? name : "UnknownError"}`;
|
|
1813
2051
|
})() : void 0;
|
|
1814
2052
|
if (isToolCallsCompletion) {
|
|
1815
|
-
|
|
1816
|
-
throw new Error("message.updated: reasoningParts missing for tool-call continuation");
|
|
2053
|
+
const responseText = reasoningText !== void 0 && reasoningText.trim().length > 0 ? reasoningText.trim() : outputText !== void 0 && outputText.trim().length > 0 ? outputText.trim() : "";
|
|
1817
2054
|
const llmAttrs = [
|
|
1818
2055
|
attrString("ai.operationId", "generateText"),
|
|
1819
|
-
attrString("ai.response.text",
|
|
1820
|
-
|
|
1821
|
-
attrString("gen_ai.request.model", modelID),
|
|
1822
|
-
attrString("gen_ai.response.model", modelID),
|
|
1823
|
-
attrInt("gen_ai.usage.input_tokens", inputTokens),
|
|
1824
|
-
attrInt("gen_ai.usage.output_tokens", outputTokens)
|
|
2056
|
+
attrString("ai.response.text", responseText),
|
|
2057
|
+
...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
|
|
1825
2058
|
];
|
|
1826
|
-
if (reasoningTokens != null && typeof reasoningTokens === "number" && reasoningTokens > 0) {
|
|
1827
|
-
llmAttrs.push(attrInt("gen_ai.usage.reasoning_tokens", reasoningTokens));
|
|
1828
|
-
}
|
|
1829
2059
|
if (state.currentSystemPrompt) {
|
|
1830
2060
|
llmAttrs.push(
|
|
1831
2061
|
attrString("gen_ai.prompt.0.role", "system"),
|
|
@@ -1860,15 +2090,8 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1860
2090
|
buildPromptMessages(state.currentSystemPrompt, state.currentInput)
|
|
1861
2091
|
),
|
|
1862
2092
|
attrString("ai.response.text", turnOutputText),
|
|
1863
|
-
|
|
1864
|
-
attrString("gen_ai.request.model", modelID),
|
|
1865
|
-
attrString("gen_ai.response.model", modelID),
|
|
1866
|
-
attrInt("gen_ai.usage.input_tokens", inputTokens),
|
|
1867
|
-
attrInt("gen_ai.usage.output_tokens", outputTokens)
|
|
2093
|
+
...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
|
|
1868
2094
|
];
|
|
1869
|
-
if (reasoningTokens != null && typeof reasoningTokens === "number" && reasoningTokens > 0) {
|
|
1870
|
-
llmAttrs.push(attrInt("gen_ai.usage.reasoning_tokens", reasoningTokens));
|
|
1871
|
-
}
|
|
1872
2095
|
if (state.currentSystemPrompt) {
|
|
1873
2096
|
llmAttrs.push(
|
|
1874
2097
|
attrString("gen_ai.prompt.0.role", "system"),
|
|
@@ -1912,19 +2135,12 @@ type: ${name != null ? name : "UnknownError"}`;
|
|
|
1912
2135
|
buildPromptMessages(state.currentSystemPrompt, state.currentInput)
|
|
1913
2136
|
),
|
|
1914
2137
|
attrString("ai.response.text", turnOutputText),
|
|
1915
|
-
|
|
1916
|
-
attrString("gen_ai.request.model", modelID),
|
|
1917
|
-
attrString("gen_ai.response.model", modelID),
|
|
1918
|
-
attrInt("gen_ai.usage.input_tokens", inputTokens),
|
|
1919
|
-
attrInt("gen_ai.usage.output_tokens", outputTokens)
|
|
2138
|
+
...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
|
|
1920
2139
|
];
|
|
1921
2140
|
if (isCompaction) {
|
|
1922
2141
|
rootAttrs.push(attrString("is_compaction", "true"));
|
|
1923
2142
|
}
|
|
1924
2143
|
rootAttrs.push(attrString("message_id", messageId));
|
|
1925
|
-
if (reasoningTokens != null && typeof reasoningTokens === "number" && reasoningTokens > 0) {
|
|
1926
|
-
rootAttrs.push(attrInt("gen_ai.usage.reasoning_tokens", reasoningTokens));
|
|
1927
|
-
}
|
|
1928
2144
|
if (state.currentSystemPrompt) {
|
|
1929
2145
|
rootAttrs.push(
|
|
1930
2146
|
attrString("gen_ai.prompt.0.role", "system"),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raindrop-ai/opencode-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Raindrop observability plugin for OpenCode — automatic session/event/span tracing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@types/node": "^20.11.17",
|
|
38
38
|
"tsup": "^8.4.0",
|
|
39
39
|
"typescript": "^5.3.3",
|
|
40
|
-
"@raindrop-ai/core": "0.
|
|
40
|
+
"@raindrop-ai/core": "0.2.0"
|
|
41
41
|
},
|
|
42
42
|
"tsup": {
|
|
43
43
|
"entry": [
|