Add configurability to set tags and metadata on langfuse traces#53
Add configurability to set tags and metadata on langfuse traces#53danchild wants to merge 1 commit into
Conversation
eedb5ca to
34b8135
Compare
- Remove inline "system_prompt_length" - Create the mechanism to pass node state to context to allow writing configured metadata and traces to langfuse traces Signed-off-by: Dan Childers <dchilder@redhat.com> Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
34b8135 to
bc14b93
Compare
Review NotesOverallGood architecture — the 1. Bug workflow nodes are not coveredThe PR only enriches context in feature workflow nodes ( The bug workflow nodes that invoke the agent were not updated:
If someone configures 2. The per-node enrichment approach is fragileThe current design requires every node that calls the agent to manually build a ~6-line context dict: context = {
"ticket_key": ticket_key,
"ticket_type": state.get("ticket_type", ""),
"current_node": state.get("current_node", ""),
"event_type": state.get("event_type", ""),
"event_source": state.get("context", {}).get("source", ""),
"retry_count": state.get("retry_count", 0),
}This is copy-pasted into 7 files and will need to be added to every future node that calls the agent. If someone forgets (as happened with the bug workflow nodes), traces from that node get no tags/metadata. Suggested alternative: Resolve the trace fields once in the orchestrator worker — it already has the full workflow state at invocation time — and store the resolved
3. Field naming inconsistency
|
Change Summary
Spec
Langfuse Trace Tags & Metadata Configurability
Date: 2026-05-20
Status: Draft
Problem
Langfuse traces created by Forge carry almost no tags or metadata today. The only metadata set is
system_prompt_length, and tags are never populated. This makes it difficult to filter, group, and analyze traces in the Langfuse dashboard by dimensions like project, ticket type, or workflow step.Solution
A configuration-driven system where admins specify which data fields to include as Langfuse trace tags and metadata via environment variables. Forge automatically resolves the configured fields from workflow state that already flows through the system. There will be no backfilling mechanism — configuration changes only affect traces created after the change.
Architecture
TracingField Enum & Resolver Registry
A
TracingFieldStrEnum insrc/forge/integrations/langfuse/fields.pydefines every field an admin can configure. Each enum member has:tag_eligibleflag indicating whether the field can be used as a tagTag eligibility rule: Categorical data (including numbers used as identifiers like
pr_number) can be used as both tags and metadata. Quantitative values meant for math (likeretry_count) are metadata-only.Available Fields
ticket_keystate["ticket_key"]AISOS-104ticket_typestate["ticket_type"]bugproject_idticket_key(prefix before-)AISOSworkflow_stepstate["current_node"]human_review_gaterepostate["current_repo"]org/repopr_numberstate["current_pr_number"]42ci_statusstate["ci_status"]passedevent_sourcestate["context"]["source"]jiraevent_typestate["event_type"]issue_updatedretry_countstate["retry_count"]2system_prompt_lengthlen(system_prompt)at agent invocation time4523llm_modelsettings.claude_modelclaude-sonnet-4-6-20250514Each resolver returns
str | None. If the data isn't present in the state, the resolver returnsNoneand the field is silently skipped for that trace.Configuration
Two environment variables following the existing
GITHUB_KNOWN_REPOScomma-separated pattern:Config Fields in
config.pyParsed Properties
Validation Behavior
Validation is per-field, not all-or-nothing. The app does not crash on invalid configuration.
TracingFieldname: log aWARNINGnaming the invalid value and listing available field names, skip it, continue parsingretry_count) appears inLANGFUSE_TRACE_TAGS: log aWARNINGexplaining why it was skipped, continueINFOline listing the configured tag fields, and oneINFOline listing the configured metadata fieldsINFOline is loggedExample log output:
Resolver Integration
New Function:
resolve_trace_fields()Located in
src/forge/integrations/langfuse/fields.py:(tags: list[str], metadata: dict[str, Any])"bug","OSASINFRA") — no field-name prefix{"ticket_type": "bug", "project_id": "OSASINFRA"})Call Site
The orchestrator worker or workflow nodes call
resolve_trace_fields(state)before invoking the agent, passing the results down:The existing hardcoded
metadata={"system_prompt_length": ...}in_run_agent()is removed.system_prompt_lengthis now a configurableTracingFieldlike any other — admins who want it add it toLANGFUSE_TRACE_METADATA. Its resolver receives the system prompt length at invocation time rather than reading from workflow state.Tag Format
Tags are raw values only — no field-name prefix. Examples:
"bug","OSASINFRA","human_review_gate". This matches the format shown in the Langfuse dashboard.Default Behavior
When
LANGFUSE_TRACE_TAGSandLANGFUSE_TRACE_METADATAare empty (the default), no tags or metadata.Files Changed
src/forge/integrations/langfuse/fields.pyTracingFieldenum, resolver functions,resolve_trace_fields()src/forge/integrations/langfuse/__init__.pysrc/forge/config.pylangfuse_trace_tags,langfuse_trace_metadatafields and parsed propertiessrc/forge/integrations/langfuse/tracing.pyget_langfuse_config()andget_langfuse_context()signatures to accept resolved tags/metadatasrc/forge/integrations/agents/agent.py_run_agent()src/forge/orchestrator/worker.pyresolve_trace_fields(state)before agent invocationtests/unit/test_langfuse_fields.pyresolve_trace_fields()Testing
Unit Tests: Field Resolvers
Each
TracingFieldresolver tested with:Unit Tests: Config Validation
LANGFUSE_TRACE_TAGSproduce warnings and get skippedTracingFieldlistsIntegration Test:
resolve_trace_fields()Given a realistic workflow state dict and configured fields, verify the correct
(tags, metadata)tuple is returned with missing fields silently omitted.Non-Goals