Repository navigation
Conversation
…ield Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… into hermes-agents-feature
…n, and relax deployment validation - Fix CreateAgentTypeAction updateOrCreate to use apps_id+name as the match key - Fix AgentType model fillable: app_id → apps_id - Add latestDeployment relation on Agent model and expose it in GraphQL schema - Relax multi_agent_list DTO type from ?string to mixed - Remove blocking validation in AgentChannelIntegrationReadinessService Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tegrationReadinessService Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces SSH polling for LLM token counts with a push-based OTel pipeline. Agent containers auto-instrument Anthropic/OpenAI calls via the OTel SDK; spans flow to an OTel Collector which aggregates gen_ai.usage.* attributes into metrics and forwards them to a new Laravel OTLP receiver stored in agent_usage_snapshots (source = '<provider>_otel'). - docker/otel/collector.yaml: OTLP gRPC receiver → spanmetrics connector → OTLP/HTTP exporter - docker/otel/otel-init.js: Node.js OTel SDK bootstrap (AnthropicInstrumentation auto-instruments LLM calls, uses KANVAS_DEPLOYMENT_ID resource attr) - docker-compose.yml: add otel-collector service (ports 4317/4318) - config/otel.php: enabled flag, collector_endpoint, internal_token - BaseDockerComposeBuilderService: inject OTEL_EXPORTER_OTLP_ENDPOINT + OTEL_SERVICE_NAME when otel.enabled=true - OtlpAdapterController: thin REST bridge (X-Internal-Token auth) that forwards OTLP JSON to GraphQL mutation - ingestAgentTelemetry mutation + IngestOtlpMetricsMutation resolver - OtlpUsageIngestionService: parses OTLP metrics JSON, resolves deployment by ID or container_name, upserts AgentUsageSnapshot - CollectDeploymentUsageCommand: skip SSH poll when otel.enabled=true - Kernel.php: enable CollectAgentTelemetryCommand cron + add hourly usage snapshot schedule Enable via: OTEL_ENABLED=true, OTEL_INTERNAL_TOKEN=<secret> Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…make inferLlmProvider public - CollectAllDeploymentsUsageCommand was referenced in Kernel.php but not committed - inferLlmProvider() changed from protected to public static so OtlpUsageIngestionService can call it Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… into feat/opentelemetry-token-usage # Conflicts: # app/Console/Kernel.php
…token usage ingestion
- Dockerfile templates: add {{OTEL_LAYER}} placeholder; buildDockerfile() expands it
to a RUN layer that installs OTel packages at /opt/otel when otel.enabled=true
- BaseLaunchAgentOnMachineAction: write otel-init.js to Docker build context when enabled
- BaseDockerComposeBuilderService: inject NODE_OPTIONS=--require /opt/otel/init.js
alongside OTEL_EXPORTER_OTLP_ENDPOINT + OTEL_SERVICE_NAME when enabled
- otel-init.js: absolute requires from /opt/otel/node_modules, bail-out guard on
OTEL_EXPORTER_OTLP_ENDPOINT, use KANVAS_DEPLOYMENT_ID resource attribute
- collector.yaml: drop spanmetrics (only produces calls/duration, not token counts);
forward raw gen_ai.* spans directly to Laravel via traces_endpoint; batch timeout 10s
- OtlpUsageIngestionService: primary path parses OTLP traces (resourceSpans) extracting
gen_ai.usage.* attributes per span, aggregated per deployment; legacy metrics path kept
for backward compat; upsertUsageSnapshot() shared by both paths; raw_output='' fix
- OtlpAdapterController: call OtlpUsageIngestionService directly (no GraphQL tunnel);
GraphQL @guard blocked system calls without auth context
- docker-compose.yml: otel-collector depends_on php so first export doesn't hit cold target
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- seed_default_tool_categories: add uuid generation to inserts - swap_tool_category_enum_for_fk: guard ADD COLUMN and DROP COLUMN with hasColumn checks - add_is_sub_agent: guard both column additions with hasColumn checks Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
CollectMachineTelemetryJobSSH usage calls when enabled@traceloop/instrumentation-anthropic; spans flow to an OTel Collector that aggregatesgen_ai.usage.*attributes and forwards metrics to a new Laravel OTLP receiveragent_usage_snapshotstable withsource = '<provider>_otel', fully backwards-compatible with SSH-polled rowsKey components
docker/otel/collector.yamlspanmetricsconnector → OTLP/HTTP exporter to Laraveldocker/otel/otel-init.jsNODE_OPTIONS=--requirein container Dockerfilesdocker-compose.ymlotel-collectorservice (ports 4317/4318)config/otel.phpenabled,collector_endpoint,internal_tokenfeature flagsBaseDockerComposeBuilderServiceOTEL_EXPORTER_OTLP_ENDPOINT+OTEL_SERVICE_NAMEinto agent containers when enabledOtlpAdapterControllerX-Internal-Token) forwarding OTLP JSON to GraphQLingestAgentTelemetrymutationOtlpUsageIngestionServiceOtlpUsageIngestionServiceAgentUsageSnapshotCollectDeploymentUsageCommandotel.enabled = trueKernel.phpCollectAgentTelemetryCommandcron (was commented out)Enabling
Add to
.env:Restart the stack —
otel-collectorwill start alongside the existing services.Containers (Dockerfile change needed)
Add this layer to each agent container Dockerfile (
openclaw,hermes):Test plan
OTEL_ENABLED=true—otel-collectorcontainer is healthyagent_usage_snapshotswithsource = 'openclaw_otel'within 30sOTEL_ENABLED=false— SSH polling still creates rows withsource = 'openclaw_docker'debugexporter incollector.yaml, checkdocker logs otel-collectorshows received spans🤖 Generated with Claude Code