feat(llm-gateway): tag helper calls with $ai_span_name for analytics#2981
Conversation
|
React Doctor found no issues in the changed files. 🎉 Reviewed by React Doctor for commit |
|
Reviews (1): Last reviewed commit: "feat(llm-gateway): tag helper calls with..." | Re-trigger Greptile |
| for (const [key, value] of Object.entries(properties)) { | ||
| if (value === null || value === undefined) continue; | ||
| const sanitized = String(value) | ||
| .replace(/[\r\n]+/g, " ") | ||
| .replace(/[^\x20-\x7e\x80-\xff]/g, ""); | ||
| headers[`x-posthog-property-${key}`] = sanitized; | ||
| } |
There was a problem hiding this comment.
Header key names are not sanitized
Values are sanitized against invalid HTTP header bytes, but the key portion of x-posthog-property-${key} is forwarded verbatim. HTTP header field names must consist only of token characters (RFC 7230 §3.2.6); a key containing spaces, colons, or control characters would produce an invalid header name that undici would reject at runtime. All current callers use ai_stage (safe), but the Record<string, ...> type places no constraint on keys, so a future caller could silently break requests.
| const sanitized = String(value) | ||
| .replace(/[\r\n]+/g, " ") | ||
| .replace(/[^\x20-\x7e\x80-\xff]/g, ""); |
There was a problem hiding this comment.
Sanitization logic duplicated from
packages/agent
The two-step regex — collapse newlines then strip non-latin1 bytes — is identical to the private sanitizeHeaderValue function in packages/agent/src/utils/gateway.ts (lines 37–39). Both functions also share the same JSDoc rationale. If the sanitization rules ever need updating (e.g. to also strip tab characters), both sites would need to change in sync. Extracting a shared utility would satisfy OnceAndOnlyOnce, assuming the package dependency direction allows it.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
good point, will re-share
4922166 to
25e2ed9
Compare
|
Reviews (2): Last reviewed commit: "feat(llm-gateway): tag helper calls with..." | Re-trigger Greptile |
Helper LLM calls (commit messages, PR descriptions) currently surface as `$ai_generation` events that are indistinguishable from the agent's main generations — they share `ai_product = posthog_code` with no further metadata, so observability queries can't filter to a specific helper.
- Add a free-form `posthogProperties` option to `LlmGatewayService.prompt()` that forwards each entry as an `x-posthog-property-<key>` header. The gateway lifts those headers onto the captured `$ai_generation` event, mirroring the agent-server's existing `buildGatewayPropertyHeaders` pattern.
- Tag the commit-message and PR-description helpers via the standard `$ai_span_name` property ("commit_message" / "pr_description") so they're filterable in LLM analytics alongside spans from elsewhere in the system.
Both the agent (ANTHROPIC_CUSTOM_HEADERS) and the new LlmGatewayService (fetch headers) need to turn a property map into x-posthog-property-* headers with the same sanitize rules. Pull the iteration + sanitize into @posthog/shared/posthog-property-headers and expose two thin adapters: buildPosthogPropertyHeaderRecord for fetch and buildPosthogPropertyHeaderLines for the SDK env-var format.
0eb1f47 to
a6f79a1
Compare
Problem
Helper LLM calls (commit messages, PR descriptions) emit
$ai_generationevents that look identical to the agent's main turns — no way to filter to just "PR description generations" or just "commit message generations" in LLM analytics.Changes
$ai_span_nameon each helper call so they can be filtered:generateCommitMessage→$ai_span_name = "commit_message"generatePrTitleAndBody→$ai_span_name = "pr_description"posthogPropertiesoption toLlmGatewayService.prompt()that forwards entries asx-posthog-property-*headers; the gateway already lifts those onto the captured event.x-posthog-property-*builder into@posthog/shared/posthog-property-headersso the agent (ANTHROPIC_CUSTOM_HEADERSlines) andLlmGatewayService(fetch headers) share one sanitize + iteration path, with two thin format adapters.Test plan
posthogPropertiesasx-posthog-property-*headers (null/undefined dropped, header values sanitized).git-pr.tscallers tagged with the right span name.