@yagni-app/code-staging 1.0.6-staging.1258.1 → 1.0.6-staging.1263.1
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/README.md
CHANGED
|
@@ -183,7 +183,14 @@ prefix instead of `claude_code`:
|
|
|
183
183
|
`yagni_code.llm_request` and `yagni_code.tool`, carrying both the flat
|
|
184
184
|
Claude Code attributes (`model`, `input_tokens`, `cost_usd`, `tool_name`)
|
|
185
185
|
and the OTel GenAI conventions (`gen_ai.*`) that LLM-observability
|
|
186
|
-
products read.
|
|
186
|
+
products read. The two count tokens differently on purpose: the flat
|
|
187
|
+
`input_tokens` / `cache_read_tokens` / `cache_creation_tokens` are disjoint,
|
|
188
|
+
exactly as Claude Code reports them, while `gen_ai.usage.input_tokens`
|
|
189
|
+
follows the semconv and includes the cached tokens, with
|
|
190
|
+
`gen_ai.usage.cache_read.input_tokens` and
|
|
191
|
+
`gen_ai.usage.cache_write.input_tokens` as subsets of it.
|
|
192
|
+
`gen_ai.cost.estimated_total` carries the same sell-rate cost as `cost_usd`
|
|
193
|
+
so vendor cost views work for the opaque tier id.
|
|
187
194
|
- **Metrics:** `yagni_code.session.count`, `token.usage` (by `type` and
|
|
188
195
|
`model`), `cost.usage`, `lines_of_code.count`, `commit.count`,
|
|
189
196
|
`pull_request.count`, `code_edit_tool.decision`, `active_time.total`.
|
|
@@ -28,10 +28,32 @@ export declare const ATTR_GEN_AI_REQUEST_MODEL = "gen_ai.request.model";
|
|
|
28
28
|
export declare const ATTR_GEN_AI_RESPONSE_MODEL = "gen_ai.response.model";
|
|
29
29
|
export declare const ATTR_GEN_AI_RESPONSE_ID = "gen_ai.response.id";
|
|
30
30
|
export declare const ATTR_GEN_AI_FINISH_REASONS = "gen_ai.response.finish_reasons";
|
|
31
|
+
/**
|
|
32
|
+
* Token usage. The semconv registry says `gen_ai.usage.input_tokens` "SHOULD
|
|
33
|
+
* include all types of input tokens, including cached tokens", and defines the
|
|
34
|
+
* cache counters as subsets of it. pi reports the Anthropic way (non-cached
|
|
35
|
+
* input, cache read, cache write as three disjoint numbers), so the tracker
|
|
36
|
+
* adds them up for the `gen_ai.*` layer. Consumers such as Datadog LLM
|
|
37
|
+
* Observability divide cache_read by input_tokens; feeding them the disjoint
|
|
38
|
+
* number produced cache ratios in the tens of thousands of percent.
|
|
39
|
+
*
|
|
40
|
+
* The flat Claude Code attributes (`input_tokens`, `cache_read_tokens`, …) and
|
|
41
|
+
* the `<prefix>.token.usage` metric deliberately keep Claude Code's disjoint
|
|
42
|
+
* semantics so cross-harness comparisons stay apples to apples.
|
|
43
|
+
*/
|
|
31
44
|
export declare const ATTR_GEN_AI_INPUT_TOKENS = "gen_ai.usage.input_tokens";
|
|
32
45
|
export declare const ATTR_GEN_AI_OUTPUT_TOKENS = "gen_ai.usage.output_tokens";
|
|
33
|
-
export declare const
|
|
34
|
-
export declare const
|
|
46
|
+
export declare const ATTR_GEN_AI_TOTAL_TOKENS = "gen_ai.usage.total_tokens";
|
|
47
|
+
export declare const ATTR_GEN_AI_CACHE_READ_TOKENS = "gen_ai.usage.cache_read.input_tokens";
|
|
48
|
+
export declare const ATTR_GEN_AI_CACHE_WRITE_TOKENS = "gen_ai.usage.cache_write.input_tokens";
|
|
49
|
+
/** Pre-semconv spellings emitted through 1.0.x; kept for one release so
|
|
50
|
+
* dashboards keyed on them keep working. */
|
|
51
|
+
export declare const ATTR_GEN_AI_CACHE_READ_TOKENS_LEGACY = "gen_ai.usage.cache_read_input_tokens";
|
|
52
|
+
export declare const ATTR_GEN_AI_CACHE_CREATION_TOKENS_LEGACY = "gen_ai.usage.cache_creation_input_tokens";
|
|
53
|
+
/** The one cost attribute Datadog's OTLP ingestion documents. Carries the
|
|
54
|
+
* same sell-rate figure as the flat `cost_usd`, so the vendor Cost view
|
|
55
|
+
* populates for the opaque tier id, which no vendor price table knows. */
|
|
56
|
+
export declare const ATTR_GEN_AI_COST_ESTIMATED_TOTAL = "gen_ai.cost.estimated_total";
|
|
35
57
|
export declare const ATTR_GEN_AI_CONVERSATION_ID = "gen_ai.conversation.id";
|
|
36
58
|
export declare const ATTR_GEN_AI_AGENT_NAME = "gen_ai.agent.name";
|
|
37
59
|
export declare const ATTR_GEN_AI_TOOL_NAME = "gen_ai.tool.name";
|
|
@@ -30,10 +30,32 @@ export const ATTR_GEN_AI_REQUEST_MODEL = "gen_ai.request.model";
|
|
|
30
30
|
export const ATTR_GEN_AI_RESPONSE_MODEL = "gen_ai.response.model";
|
|
31
31
|
export const ATTR_GEN_AI_RESPONSE_ID = "gen_ai.response.id";
|
|
32
32
|
export const ATTR_GEN_AI_FINISH_REASONS = "gen_ai.response.finish_reasons";
|
|
33
|
+
/**
|
|
34
|
+
* Token usage. The semconv registry says `gen_ai.usage.input_tokens` "SHOULD
|
|
35
|
+
* include all types of input tokens, including cached tokens", and defines the
|
|
36
|
+
* cache counters as subsets of it. pi reports the Anthropic way (non-cached
|
|
37
|
+
* input, cache read, cache write as three disjoint numbers), so the tracker
|
|
38
|
+
* adds them up for the `gen_ai.*` layer. Consumers such as Datadog LLM
|
|
39
|
+
* Observability divide cache_read by input_tokens; feeding them the disjoint
|
|
40
|
+
* number produced cache ratios in the tens of thousands of percent.
|
|
41
|
+
*
|
|
42
|
+
* The flat Claude Code attributes (`input_tokens`, `cache_read_tokens`, …) and
|
|
43
|
+
* the `<prefix>.token.usage` metric deliberately keep Claude Code's disjoint
|
|
44
|
+
* semantics so cross-harness comparisons stay apples to apples.
|
|
45
|
+
*/
|
|
33
46
|
export const ATTR_GEN_AI_INPUT_TOKENS = "gen_ai.usage.input_tokens";
|
|
34
47
|
export const ATTR_GEN_AI_OUTPUT_TOKENS = "gen_ai.usage.output_tokens";
|
|
35
|
-
export const
|
|
36
|
-
export const
|
|
48
|
+
export const ATTR_GEN_AI_TOTAL_TOKENS = "gen_ai.usage.total_tokens";
|
|
49
|
+
export const ATTR_GEN_AI_CACHE_READ_TOKENS = "gen_ai.usage.cache_read.input_tokens";
|
|
50
|
+
export const ATTR_GEN_AI_CACHE_WRITE_TOKENS = "gen_ai.usage.cache_write.input_tokens";
|
|
51
|
+
/** Pre-semconv spellings emitted through 1.0.x; kept for one release so
|
|
52
|
+
* dashboards keyed on them keep working. */
|
|
53
|
+
export const ATTR_GEN_AI_CACHE_READ_TOKENS_LEGACY = "gen_ai.usage.cache_read_input_tokens";
|
|
54
|
+
export const ATTR_GEN_AI_CACHE_CREATION_TOKENS_LEGACY = "gen_ai.usage.cache_creation_input_tokens";
|
|
55
|
+
/** The one cost attribute Datadog's OTLP ingestion documents. Carries the
|
|
56
|
+
* same sell-rate figure as the flat `cost_usd`, so the vendor Cost view
|
|
57
|
+
* populates for the opaque tier id, which no vendor price table knows. */
|
|
58
|
+
export const ATTR_GEN_AI_COST_ESTIMATED_TOTAL = "gen_ai.cost.estimated_total";
|
|
37
59
|
export const ATTR_GEN_AI_CONVERSATION_ID = "gen_ai.conversation.id";
|
|
38
60
|
export const ATTR_GEN_AI_AGENT_NAME = "gen_ai.agent.name";
|
|
39
61
|
export const ATTR_GEN_AI_TOOL_NAME = "gen_ai.tool.name";
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
import { randomUUID } from "node:crypto";
|
|
21
21
|
import { context as otelContext, SpanStatusCode, trace, } from "@opentelemetry/api";
|
|
22
22
|
import { SeverityNumber } from "@opentelemetry/api-logs";
|
|
23
|
-
import { ATTR_APP_ENTRYPOINT, ATTR_APP_VERSION, ATTR_ERROR_TYPE, ATTR_GEN_AI_AGENT_NAME,
|
|
23
|
+
import { ATTR_APP_ENTRYPOINT, ATTR_APP_VERSION, ATTR_ERROR_TYPE, ATTR_GEN_AI_AGENT_NAME, ATTR_GEN_AI_CACHE_CREATION_TOKENS_LEGACY, ATTR_GEN_AI_CACHE_READ_TOKENS, ATTR_GEN_AI_CACHE_READ_TOKENS_LEGACY, ATTR_GEN_AI_CACHE_WRITE_TOKENS, ATTR_GEN_AI_CONVERSATION_ID, ATTR_GEN_AI_COST_ESTIMATED_TOTAL, ATTR_GEN_AI_FINISH_REASONS, ATTR_GEN_AI_INPUT_TOKENS, ATTR_GEN_AI_OPERATION_NAME, ATTR_GEN_AI_OUTPUT_TOKENS, ATTR_GEN_AI_TOTAL_TOKENS, ATTR_GEN_AI_PROVIDER_NAME, ATTR_GEN_AI_REQUEST_MODEL, ATTR_GEN_AI_RESPONSE_ID, ATTR_GEN_AI_RESPONSE_MODEL, ATTR_GEN_AI_SYSTEM, ATTR_GEN_AI_TOOL_CALL_ID, ATTR_GEN_AI_TOOL_NAME, ATTR_GEN_AI_TOOL_TYPE, ATTR_HTTP_STATUS_CODE, ATTR_ORGANIZATION_ID, ATTR_SESSION_ID, ATTR_TERMINAL_TYPE, ATTR_USER_EMAIL, EVENT_API_ERROR, EVENT_API_REQUEST, EVENT_ASSISTANT_RESPONSE, EVENT_PERMISSION_MODE_CHANGED, EVENT_TOOL_DECISION, EVENT_TOOL_RESULT, EVENT_USER_PROMPT, GEN_AI_PROVIDER, languageFromPath, METRIC_ACTIVE_TIME, METRIC_CODE_EDIT_DECISION, METRIC_COMMIT_COUNT, METRIC_COST_USAGE, METRIC_LINES_OF_CODE, METRIC_PULL_REQUEST_COUNT, METRIC_SESSION_COUNT, METRIC_TOKEN_USAGE, PREFIX, SPAN_INTERACTION, SPAN_LLM_REQUEST, SPAN_TOOL, SPAN_TURN, } from "./attrs.js";
|
|
24
24
|
/** Idle cutoff for user active time: gaps longer than this are not "active". */
|
|
25
25
|
export const USER_ACTIVE_IDLE_CUTOFF_MS = 5 * 60 * 1000;
|
|
26
26
|
const EDIT_TOOLS = new Set(["edit", "write", "multi_edit", "notebook_edit"]);
|
|
@@ -276,19 +276,33 @@ export class SessionTelemetry {
|
|
|
276
276
|
const output = num(usage.output);
|
|
277
277
|
const cacheRead = num(usage.cacheRead);
|
|
278
278
|
const cacheCreation = num(usage.cacheWrite);
|
|
279
|
+
// Claude Code always reports cost_usd (0 when it has nothing), so the flat
|
|
280
|
+
// attribute and the counter keep that shape. The vendor cost attribute is
|
|
281
|
+
// different: a 0 there renders as "$0" in cost views, so it is only set
|
|
282
|
+
// when pi actually priced the request.
|
|
283
|
+
const costKnown = typeof usage.cost === "number" || typeof usage.cost?.total === "number";
|
|
279
284
|
const cost = typeof usage.cost === "number" ? usage.cost : num(usage.cost?.total);
|
|
280
285
|
const stop = message.stopReason ?? "stop";
|
|
281
286
|
const isError = stop === "error";
|
|
282
287
|
const requestId = message.responseId ?? slot.requestId;
|
|
288
|
+
// pi's `input` excludes cached tokens (Anthropic semantics). The semconv
|
|
289
|
+
// `gen_ai.usage.input_tokens` includes them, with the cache counters as
|
|
290
|
+
// subsets; see attrs.ts. The flat Claude Code attributes below stay disjoint.
|
|
291
|
+
const inclusiveInput = input + cacheRead + cacheCreation;
|
|
283
292
|
const span = slot.span;
|
|
284
293
|
if (message.model)
|
|
285
294
|
span.setAttribute(ATTR_GEN_AI_REQUEST_MODEL, message.model);
|
|
286
295
|
span.setAttribute(ATTR_GEN_AI_RESPONSE_MODEL, model);
|
|
287
296
|
span.setAttribute("model", model);
|
|
288
|
-
span.setAttribute(ATTR_GEN_AI_INPUT_TOKENS,
|
|
297
|
+
span.setAttribute(ATTR_GEN_AI_INPUT_TOKENS, inclusiveInput);
|
|
289
298
|
span.setAttribute(ATTR_GEN_AI_OUTPUT_TOKENS, output);
|
|
299
|
+
span.setAttribute(ATTR_GEN_AI_TOTAL_TOKENS, inclusiveInput + output);
|
|
290
300
|
span.setAttribute(ATTR_GEN_AI_CACHE_READ_TOKENS, cacheRead);
|
|
291
|
-
span.setAttribute(
|
|
301
|
+
span.setAttribute(ATTR_GEN_AI_CACHE_WRITE_TOKENS, cacheCreation);
|
|
302
|
+
span.setAttribute(ATTR_GEN_AI_CACHE_READ_TOKENS_LEGACY, cacheRead);
|
|
303
|
+
span.setAttribute(ATTR_GEN_AI_CACHE_CREATION_TOKENS_LEGACY, cacheCreation);
|
|
304
|
+
if (costKnown)
|
|
305
|
+
span.setAttribute(ATTR_GEN_AI_COST_ESTIMATED_TOTAL, cost);
|
|
292
306
|
span.setAttribute("input_tokens", input);
|
|
293
307
|
span.setAttribute("output_tokens", output);
|
|
294
308
|
span.setAttribute("cache_read_tokens", cacheRead);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yagni-app/code-staging",
|
|
3
|
-
"version": "1.0.6-staging.
|
|
3
|
+
"version": "1.0.6-staging.1263.1",
|
|
4
4
|
"description": "YAGNI Code: a terminal coding agent that already knows your company. One YAGNI login routes the model and grounds the agent in your team's context.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"author": "YAGNI, Inc. <jack@yagni.app> (https://yagni.app)",
|
|
@@ -58,5 +58,5 @@
|
|
|
58
58
|
"turndown": "^7.2.4",
|
|
59
59
|
"typebox": "^1.3.15"
|
|
60
60
|
},
|
|
61
|
-
"yagniSourceSha": "
|
|
61
|
+
"yagniSourceSha": "1dff831f8fbc3e18306912ba490afee7d5caa9c1"
|
|
62
62
|
}
|