agentevals-cli 0.9.9__tar.gz → 0.9.10__tar.gz

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.
Files changed (297) hide show
  1. agentevals_cli-0.9.10/.github/workflows/ci.yml +139 -0
  2. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/Dockerfile +1 -1
  3. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/Makefile +8 -1
  4. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/PKG-INFO +1 -1
  5. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/api/otlp_processing.py +12 -31
  6. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/extraction.py +7 -14
  7. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/loader/otlp.py +6 -21
  8. agentevals_cli-0.9.10/src/agentevals/otlp_anyvalue.py +118 -0
  9. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/trace_attrs.py +30 -0
  10. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/test_extraction.py +124 -0
  11. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/test_otlp_loader.py +156 -0
  12. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/test_otlp_receiver.py +54 -0
  13. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/package-lock.json +87 -87
  14. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/package.json +7 -7
  15. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/uv.lock +6 -9
  16. agentevals_cli-0.9.9/.github/workflows/ci.yml +0 -86
  17. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/.claude/skills/eval/SKILL.md +0 -0
  18. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/.claude/skills/eval/evals/evals.json +0 -0
  19. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/.claude/skills/inspect/SKILL.md +0 -0
  20. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/.claude/skills/inspect/evals/evals.json +0 -0
  21. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/.dockerignore +0 -0
  22. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  23. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  24. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  25. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/.github/dependabot.yml +0 -0
  26. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/.github/workflows/publish-evaluator-sdk.yml +0 -0
  27. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/.github/workflows/release.yml +0 -0
  28. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/.gitignore +0 -0
  29. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/.mcp.json +0 -0
  30. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/CONTRIBUTING.md +0 -0
  31. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/DEVELOPMENT.md +0 -0
  32. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/LICENSE +0 -0
  33. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/README.md +0 -0
  34. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/charts/agentevals/Chart.yaml +0 -0
  35. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/charts/agentevals/templates/NOTES.txt +0 -0
  36. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/charts/agentevals/templates/_helpers.tpl +0 -0
  37. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/charts/agentevals/templates/deployment.yaml +0 -0
  38. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/charts/agentevals/templates/postgresql-secret.yaml +0 -0
  39. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/charts/agentevals/templates/postgresql.yaml +0 -0
  40. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/charts/agentevals/templates/rbac.yaml +0 -0
  41. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/charts/agentevals/templates/service.yaml +0 -0
  42. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/charts/agentevals/templates/serviceaccount.yaml +0 -0
  43. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/charts/agentevals/values.yaml +0 -0
  44. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/docs/assets/logo-color-on-transparent.svg +0 -0
  45. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/docs/assets/logo-color.png +0 -0
  46. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/docs/assets/logo-dark-on-transparent.svg +0 -0
  47. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/docs/custom-evaluators.md +0 -0
  48. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/docs/eval-set-format.md +0 -0
  49. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/docs/otel-compatibility.md +0 -0
  50. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/docs/run-history.md +0 -0
  51. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/docs/streaming.md +0 -0
  52. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/README.md +0 -0
  53. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/custom_evaluators/eval_config.yaml +0 -0
  54. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/custom_evaluators/eval_config_openai_eval.yaml +0 -0
  55. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/custom_evaluators/response_quality.py +0 -0
  56. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/custom_evaluators/tool_call_checker.py +0 -0
  57. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/custom_sink/README.md +0 -0
  58. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/custom_sink/agentevals_example_custom_sink/__init__.py +0 -0
  59. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/custom_sink/agentevals_example_custom_sink/sink.py +0 -0
  60. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/custom_sink/pyproject.toml +0 -0
  61. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/dice_agent/README.md +0 -0
  62. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/dice_agent/agent.py +0 -0
  63. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/dice_agent/eval_set.json +0 -0
  64. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/dice_agent/main.py +0 -0
  65. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/dice_agent/test_streaming.py +0 -0
  66. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/kubernetes/README.md +0 -0
  67. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/langchain_agent/README.md +0 -0
  68. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/langchain_agent/agent.py +0 -0
  69. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/langchain_agent/eval_set.json +0 -0
  70. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/langchain_agent/main.py +0 -0
  71. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/langchain_agent/requirements.txt +0 -0
  72. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/langchain_agent/test_streaming.py +0 -0
  73. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/sdk_example/async_example.py +0 -0
  74. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/sdk_example/context_manager_example.py +0 -0
  75. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/sdk_example/decorator_example.py +0 -0
  76. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/sdk_example/requirements.txt +0 -0
  77. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/strands_agent/agent.py +0 -0
  78. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/strands_agent/eval_set.json +0 -0
  79. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/strands_agent/main.py +0 -0
  80. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/strands_agent/requirements.txt +0 -0
  81. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/zero-code-examples/adk/requirements.txt +0 -0
  82. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/zero-code-examples/adk/run.py +0 -0
  83. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/zero-code-examples/langchain/requirements.txt +0 -0
  84. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/zero-code-examples/langchain/run.py +0 -0
  85. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/zero-code-examples/ollama/requirements.txt +0 -0
  86. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/zero-code-examples/ollama/run.py +0 -0
  87. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/zero-code-examples/openai-agents/requirements.txt +0 -0
  88. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/zero-code-examples/openai-agents/run.py +0 -0
  89. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/zero-code-examples/pydantic-ai/requirements.txt +0 -0
  90. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/zero-code-examples/pydantic-ai/run.py +0 -0
  91. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/zero-code-examples/strands/requirements.txt +0 -0
  92. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/examples/zero-code-examples/strands/run.py +0 -0
  93. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/flake.lock +0 -0
  94. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/flake.nix +0 -0
  95. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/packages/evaluator-sdk-py/README.md +0 -0
  96. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/packages/evaluator-sdk-py/pyproject.toml +0 -0
  97. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/packages/evaluator-sdk-py/src/agentevals_evaluator_sdk/__init__.py +0 -0
  98. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/packages/evaluator-sdk-py/src/agentevals_evaluator_sdk/decorator.py +0 -0
  99. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/packages/evaluator-sdk-py/src/agentevals_evaluator_sdk/types.py +0 -0
  100. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/pyproject.toml +0 -0
  101. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/samples/eval_set_helm.json +0 -0
  102. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/samples/evalset_helm_3_2026-02-23.json +0 -0
  103. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/samples/evalset_k8s_2026-02-20.json +0 -0
  104. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/samples/helm.json +0 -0
  105. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/samples/helm_2.json +0 -0
  106. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/samples/helm_3.json +0 -0
  107. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/samples/k8s.json +0 -0
  108. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/samples/tempo_export_with_batches.json +0 -0
  109. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/__init__.py +0 -0
  110. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/_protocol.py +0 -0
  111. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/_static/assets/index-BqibLiHO.css +0 -0
  112. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/_static/assets/index-ClNj3OAl.js +0 -0
  113. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/_static/index.html +0 -0
  114. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/_static/logo.svg +0 -0
  115. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/_static/vite.svg +0 -0
  116. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/api/__init__.py +0 -0
  117. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/api/app.py +0 -0
  118. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/api/debug_routes.py +0 -0
  119. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/api/dependencies.py +0 -0
  120. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/api/models.py +0 -0
  121. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/api/otlp_app.py +0 -0
  122. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/api/otlp_grpc.py +0 -0
  123. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/api/otlp_routes.py +0 -0
  124. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/api/routes.py +0 -0
  125. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/api/runs_routes.py +0 -0
  126. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/api/streaming_routes.py +0 -0
  127. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/builtin_metrics.py +0 -0
  128. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/cli.py +0 -0
  129. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/config.py +0 -0
  130. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/converter.py +0 -0
  131. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/custom_evaluators.py +0 -0
  132. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/eval_config_loader.py +0 -0
  133. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/evaluator/__init__.py +0 -0
  134. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/evaluator/resolver.py +0 -0
  135. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/evaluator/sources.py +0 -0
  136. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/evaluator/templates.py +0 -0
  137. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/evaluator/venv.py +0 -0
  138. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/genai_converter.py +0 -0
  139. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/loader/__init__.py +0 -0
  140. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/loader/auto.py +0 -0
  141. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/loader/base.py +0 -0
  142. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/loader/jaeger.py +0 -0
  143. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/mcp_server.py +0 -0
  144. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/openai_eval_backend.py +0 -0
  145. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/output.py +0 -0
  146. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/resolvers/__init__.py +0 -0
  147. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/resolvers/kubernetes.py +0 -0
  148. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/run/__init__.py +0 -0
  149. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/run/fetcher.py +0 -0
  150. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/run/result_builder.py +0 -0
  151. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/run/service.py +0 -0
  152. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/run/sinks.py +0 -0
  153. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/run/worker.py +0 -0
  154. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/runner.py +0 -0
  155. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/sdk.py +0 -0
  156. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/storage/__init__.py +0 -0
  157. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/storage/config.py +0 -0
  158. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/storage/models.py +0 -0
  159. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/storage/postgres/__init__.py +0 -0
  160. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/storage/postgres/migrations/000001_init.down.sql +0 -0
  161. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/storage/postgres/migrations/000001_init.up.sql +0 -0
  162. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/storage/postgres/migrator.py +0 -0
  163. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/storage/postgres/pool.py +0 -0
  164. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/storage/repos/__init__.py +0 -0
  165. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/storage/repos/memory.py +0 -0
  166. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/storage/repos/postgres.py +0 -0
  167. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/streaming/__init__.py +0 -0
  168. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/streaming/exports.py +0 -0
  169. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/streaming/incremental_processor.py +0 -0
  170. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/streaming/processor.py +0 -0
  171. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/streaming/session.py +0 -0
  172. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/streaming/ws_server.py +0 -0
  173. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/trace_metrics.py +0 -0
  174. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/utils/__init__.py +0 -0
  175. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/utils/genai_messages.py +0 -0
  176. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/utils/log_buffer.py +0 -0
  177. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/src/agentevals/utils/log_enrichment.py +0 -0
  178. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/api/__init__.py +0 -0
  179. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/api/test_evaluate_persistence.py +0 -0
  180. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/api/test_runs_routes.py +0 -0
  181. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/integration/__init__.py +0 -0
  182. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/integration/conftest.py +0 -0
  183. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/integration/test_evaluation_pipeline.py +0 -0
  184. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/integration/test_live_agents.py +0 -0
  185. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/integration/test_otlp_grpc_receiver.py +0 -0
  186. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/integration/test_session_grouping.py +0 -0
  187. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/integration/test_timing_stress.py +0 -0
  188. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/resolvers/__init__.py +0 -0
  189. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/resolvers/test_kubernetes.py +0 -0
  190. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/resolvers/test_registry.py +0 -0
  191. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/run/__init__.py +0 -0
  192. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/run/test_fetcher.py +0 -0
  193. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/run/test_result_builder.py +0 -0
  194. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/run/test_service.py +0 -0
  195. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/run/test_sinks.py +0 -0
  196. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/storage/__init__.py +0 -0
  197. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/storage/test_config.py +0 -0
  198. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/storage/test_memory_repos.py +0 -0
  199. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/storage/test_migrator.py +0 -0
  200. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/storage/test_models.py +0 -0
  201. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/test_api.py +0 -0
  202. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/test_cli.py +0 -0
  203. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/test_converter.py +0 -0
  204. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/test_credential_injection.py +0 -0
  205. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/test_eval_config_loader.py +0 -0
  206. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/test_genai_converter.py +0 -0
  207. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/test_incremental_processor.py +0 -0
  208. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/test_jaeger_loader.py +0 -0
  209. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/test_loader_auto.py +0 -0
  210. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/test_log_enrichment.py +0 -0
  211. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/test_mcp_server.py +0 -0
  212. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/test_openai_eval_backend.py +0 -0
  213. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/test_output.py +0 -0
  214. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/test_protocol.py +0 -0
  215. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/test_runner.py +0 -0
  216. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/test_sdk.py +0 -0
  217. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/tests/test_trace_metrics.py +0 -0
  218. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/.gitignore +0 -0
  219. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/README.md +0 -0
  220. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/eslint.config.js +0 -0
  221. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/index.html +0 -0
  222. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/public/logo.svg +0 -0
  223. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/public/vite.svg +0 -0
  224. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/App.css +0 -0
  225. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/App.tsx +0 -0
  226. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/api/client.ts +0 -0
  227. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/assets/react.svg +0 -0
  228. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/annotation-queue/AnnotationDetailPanel.tsx +0 -0
  229. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/annotation-queue/AnnotationQueueView.tsx +0 -0
  230. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/annotation-queue/AnnotationTable.tsx +0 -0
  231. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/bug-report/BugReportModal.tsx +0 -0
  232. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/builder/BuilderHeader.tsx +0 -0
  233. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/builder/BuilderView.tsx +0 -0
  234. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/builder/EvalCaseCard.tsx +0 -0
  235. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/builder/EvalCasesList.tsx +0 -0
  236. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/builder/InvocationEditor.tsx +0 -0
  237. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/builder/JsonPreview.tsx +0 -0
  238. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/builder/MetadataEditor.tsx +0 -0
  239. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/builder/TraceUploadZone.tsx +0 -0
  240. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/builder/index.ts +0 -0
  241. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/dashboard/DashboardView.tsx +0 -0
  242. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/dashboard/MetricScoreCard.tsx +0 -0
  243. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/dashboard/PerformanceCard.tsx +0 -0
  244. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/dashboard/PerformanceCharts.tsx +0 -0
  245. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/dashboard/SummaryStats.tsx +0 -0
  246. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/dashboard/TraceCard.tsx +0 -0
  247. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/dashboard/TraceTable.tsx +0 -0
  248. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/inspector/ComparisonPanel.tsx +0 -0
  249. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/inspector/DataSection.tsx +0 -0
  250. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/inspector/InspectorHeader.tsx +0 -0
  251. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/inspector/InspectorLayout.tsx +0 -0
  252. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/inspector/InspectorView.tsx +0 -0
  253. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/inspector/InvocationCard.tsx +0 -0
  254. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/inspector/InvocationSummaryPanel.tsx +0 -0
  255. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/inspector/MetricResultsSection.tsx +0 -0
  256. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/inspector/MetricsComparisonSection.tsx +0 -0
  257. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/inspector/PerformanceSection.tsx +0 -0
  258. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/inspector/ToolCallList.tsx +0 -0
  259. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/inspector/TrajectoryComparisonDetails.tsx +0 -0
  260. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/runs/PassRateTrendChart.tsx +0 -0
  261. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/runs/PerMetricTrendChart.tsx +0 -0
  262. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/runs/RunDetailView.tsx +0 -0
  263. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/runs/RunsHistoryTable.tsx +0 -0
  264. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/runs/RunsView.tsx +0 -0
  265. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/runs/runHistory.ts +0 -0
  266. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/sidebar/Sidebar.tsx +0 -0
  267. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/streaming/LiveConversationPanel.tsx +0 -0
  268. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/streaming/LiveMessage.tsx +0 -0
  269. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/streaming/LiveStreamingView.tsx +0 -0
  270. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/streaming/SessionCard.tsx +0 -0
  271. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/streaming/SessionMetadata.tsx +0 -0
  272. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/upload/EvalSetEditorDrawer.tsx +0 -0
  273. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/upload/FileDropZone.tsx +0 -0
  274. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/upload/MetricSelector.tsx +0 -0
  275. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/upload/RawJsonPreview.tsx +0 -0
  276. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/upload/TraceEditorDrawer.tsx +0 -0
  277. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/upload/UploadView.tsx +0 -0
  278. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/components/welcome/WelcomeView.tsx +0 -0
  279. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/config.ts +0 -0
  280. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/context/TraceContext.tsx +0 -0
  281. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/context/TraceProvider.tsx +0 -0
  282. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/index.css +0 -0
  283. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/lib/console-capture.ts +0 -0
  284. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/lib/eval-config.ts +0 -0
  285. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/lib/evalset-builder.ts +0 -0
  286. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/lib/network-capture.ts +0 -0
  287. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/lib/trace-helpers.ts +0 -0
  288. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/lib/trace-loader.ts +0 -0
  289. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/lib/trace-metadata.ts +0 -0
  290. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/lib/trace-patcher.ts +0 -0
  291. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/lib/types.ts +0 -0
  292. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/lib/utils.ts +0 -0
  293. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/src/main.tsx +0 -0
  294. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/tsconfig.app.json +0 -0
  295. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/tsconfig.json +0 -0
  296. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/tsconfig.node.json +0 -0
  297. {agentevals_cli-0.9.9 → agentevals_cli-0.9.10}/ui/vite.config.ts +0 -0
@@ -0,0 +1,139 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+ push:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ helm-chart:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
17
+
18
+ - name: Lint + template
19
+ run: make helm-test
20
+
21
+ lint:
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
25
+ with:
26
+ fetch-depth: 0
27
+
28
+ - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
29
+ with:
30
+ enable-cache: true
31
+
32
+ - name: Verify uv.lock matches pyproject.toml
33
+ run: uv lock --check
34
+
35
+ - name: Install dependencies
36
+ run: |
37
+ uv venv
38
+ uv pip install setuptools
39
+ uv sync --dev
40
+
41
+ - name: Ruff check
42
+ run: uv run ruff check .
43
+
44
+ - name: Ruff format check
45
+ run: uv run ruff format --check .
46
+
47
+ ui:
48
+ runs-on: ubuntu-latest
49
+ steps:
50
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
51
+
52
+ - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
53
+ with:
54
+ node-version: '22'
55
+ cache: npm
56
+ cache-dependency-path: ui/package-lock.json
57
+
58
+ # tsc -b && vite build, same as the release workflow bundles.
59
+ - name: Build UI
60
+ run: make build-ui
61
+
62
+ docker:
63
+ runs-on: ubuntu-latest
64
+ permissions:
65
+ contents: read
66
+ env:
67
+ IMAGE_REF: agentevals:ci
68
+ IMAGE_VERSION: 0.0.0.dev0
69
+ steps:
70
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
71
+
72
+ - uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
73
+
74
+ # The release workflow is the only other place this Dockerfile gets built, and it
75
+ # runs after the GitHub release is already published. Build it here so a break
76
+ # lands on the PR instead of halfway through a release.
77
+ # Single-arch keeps it native; the multi-arch build stays release-only because
78
+ # emulating arm64 through the node and uv layers is an order of magnitude slower.
79
+ - name: Build image
80
+ uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
81
+ with:
82
+ context: .
83
+ platforms: linux/amd64
84
+ build-args: VERSION=${{ env.IMAGE_VERSION }}
85
+ tags: ${{ env.IMAGE_REF }}
86
+ push: false
87
+ load: true
88
+ cache-from: type=gha
89
+ # Pull requests read main's layers but never write, so a branch cannot grow
90
+ # or displace the cache the other jobs share.
91
+ cache-to: ${{ github.event_name == 'push' && 'type=gha,mode=max' || '' }}
92
+
93
+ - name: Smoke test
94
+ run: |
95
+ docker run -d --name agentevals-ci -p 127.0.0.1:8001:8001 "$IMAGE_REF"
96
+ for _ in $(seq 1 30); do
97
+ if curl -fsS --max-time 3 http://127.0.0.1:8001/api/health -o "$RUNNER_TEMP/health.json"; then
98
+ break
99
+ fi
100
+ sleep 2
101
+ done
102
+ test -s "$RUNNER_TEMP/health.json" || { echo "server never became healthy"; exit 1; }
103
+ cat "$RUNNER_TEMP/health.json"
104
+ grep -q '"status":"ok"' "$RUNNER_TEMP/health.json"
105
+ # Proves the SETUPTOOLS_SCM_PRETEND_VERSION path still resolves; a silent
106
+ # break there would ship a mis-versioned image.
107
+ grep -q "\"version\":\"$IMAGE_VERSION\"" "$RUNNER_TEMP/health.json"
108
+ curl -fsS http://127.0.0.1:8001/ | grep -q '<div id="root">'
109
+ test "$(docker exec agentevals-ci id -u)" = "1000"
110
+
111
+ - name: Container logs
112
+ if: failure()
113
+ run: docker logs agentevals-ci
114
+
115
+ test:
116
+ runs-on: ubuntu-latest
117
+ strategy:
118
+ matrix:
119
+ python-version: ["3.11", "3.12", "3.13"]
120
+ steps:
121
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
122
+ with:
123
+ fetch-depth: 0
124
+
125
+ - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
126
+ with:
127
+ enable-cache: true
128
+
129
+ - name: Install Python ${{ matrix.python-version }}
130
+ run: uv python install ${{ matrix.python-version }}
131
+
132
+ - name: Install dependencies
133
+ run: |
134
+ uv venv --python ${{ matrix.python-version }}
135
+ uv pip install setuptools
136
+ uv sync --dev --all-extras --frozen --python ${{ matrix.python-version }}
137
+
138
+ - name: Run tests
139
+ run: uv run pytest -m "not integration and not e2e" --tb=short -q
@@ -1,6 +1,6 @@
1
1
  # syntax=docker/dockerfile:1
2
2
 
3
- FROM node:25-bookworm-slim AS ui
3
+ FROM node:26-bookworm-slim AS ui
4
4
  WORKDIR /build/ui
5
5
  COPY ui/package.json ui/package-lock.json ./
6
6
  # Skip lifecycle scripts during ci, then rebuild esbuild in its own layer — avoids ETXTBSY when
@@ -9,13 +9,17 @@ DOCKER_IMAGE_REF := $(if $(DOCKER_REGISTRY),$(DOCKER_REGISTRY:%/=%)/$(DOCKER_IMA
9
9
  # Multi-arch build (requires docker buildx). Manifest lists must be pushed — use build-docker-local for a single-arch --load.
10
10
  PLATFORMS ?= linux/amd64,linux/arm64
11
11
 
12
+ # LOCAL_VERSION falls back to a stub so build-docker-local works without uv or git history.
13
+ LOCAL_VERSION ?= $(if $(VERSION),$(VERSION),0.0.0.dev0)
14
+ LOCAL_TAG ?= local
15
+
12
16
  HELM_REPO ?= oci://ghcr.io/agentevals-dev/agentevals
13
17
  HELM_DIST_FOLDER ?= dist/helm
14
18
  HELM_CHART_DIR ?= charts/agentevals
15
19
  HELM_CHART_OCI_URL ?= $(HELM_REPO)/helm
16
20
  HELM_CHART_VERSION ?= $(VERSION)
17
21
 
18
- .PHONY: build build-bundle build-docker build-ui release clean dev-backend dev-backend-pg dev-frontend dev-bundle pg-up pg-down migrate test test-unit test-integration test-e2e helm-lint helm-template helm-test helm-cleanup helm-package helm-publish
22
+ .PHONY: build build-bundle build-docker build-docker-local build-ui release clean dev-backend dev-backend-pg dev-frontend dev-bundle pg-up pg-down migrate test test-unit test-integration test-e2e helm-lint helm-template helm-test helm-cleanup helm-package helm-publish
19
23
 
20
24
  PG_CONTAINER ?= agentevals-pg
21
25
  PG_PORT ?= 5432
@@ -31,6 +35,9 @@ build-docker:
31
35
  @test -n "$(VERSION)" || { echo "ERROR: VERSION is empty. Pass VERSION=x.y.z explicitly, or install uv so hatch-vcs can resolve it."; exit 1; }
32
36
  docker buildx build --platform $(PLATFORMS) --build-arg VERSION=$(VERSION) -t $(DOCKER_IMAGE_REF):$(DOCKER_TAG) --push .
33
37
 
38
+ build-docker-local:
39
+ docker buildx build --build-arg VERSION=$(LOCAL_VERSION) -t $(DOCKER_IMAGE_REF):$(LOCAL_TAG) --load .
40
+
34
41
  build-ui:
35
42
  cd ui && npm ci && npm run build
36
43
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agentevals-cli
3
- Version: 0.9.9
3
+ Version: 0.9.10
4
4
  Summary: Standalone framework to evaluate agent correctness based on portable OpenTelemetry traces
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.11
@@ -15,7 +15,10 @@ from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import (
15
15
  )
16
16
 
17
17
  from ..extraction import flatten_otlp_attributes
18
+ from ..otlp_anyvalue import decode_any_value
18
19
  from ..trace_attrs import (
20
+ AGENTEVALS_EVAL_SET_ID,
21
+ AGENTEVALS_SESSION_NAME,
19
22
  OTEL_GENAI_CONVERSATION_ID,
20
23
  OTEL_GENAI_INPUT_MESSAGES,
21
24
  OTEL_GENAI_OUTPUT_MESSAGES,
@@ -30,9 +33,6 @@ if TYPE_CHECKING:
30
33
 
31
34
  logger = logging.getLogger(__name__)
32
35
 
33
- AGENTEVALS_EVAL_SET_ID = "agentevals.eval_set_id"
34
- AGENTEVALS_SESSION_NAME = "agentevals.session_name"
35
-
36
36
 
37
37
  async def process_traces(body: dict, manager: StreamingTraceManager) -> None:
38
38
  """Parse ExportTraceServiceRequest and feed spans to the pipeline."""
@@ -242,7 +242,13 @@ def _normalize_span(span_data: dict, scope_name: str, scope_version: str, schema
242
242
 
243
243
 
244
244
  def _extract_agentevals_metadata(resource_attrs: list[dict]) -> dict:
245
- """Extract agentevals-specific metadata from OTLP resource attributes."""
245
+ """Extract agentevals-specific metadata from OTLP resource attributes.
246
+
247
+ ``eval_set_id`` and ``session_name`` are used as dict keys downstream
248
+ (``_active_session_for_name``) and typed ``str | None`` on the session
249
+ models. Neither is in ``SPEC_CONTAINER_ATTRS``, so the shared decoder never
250
+ hands them a list or dict in the first place.
251
+ """
246
252
  flat = flatten_otlp_attributes(resource_attrs)
247
253
  return {
248
254
  "eval_set_id": flat.get(AGENTEVALS_EVAL_SET_ID),
@@ -310,37 +316,12 @@ def _convert_otlp_log_record(log_record: dict) -> dict | None:
310
316
  return result
311
317
 
312
318
 
313
- def _parse_otlp_any_value(value_obj: dict):
314
- """Recursively parse an OTLP AnyValue to native Python types.
315
-
316
- Handles the full AnyValue union: stringValue, intValue, doubleValue,
317
- boolValue, kvlistValue (→ dict), arrayValue (→ list), bytesValue.
318
- """
319
- if "stringValue" in value_obj:
320
- return value_obj["stringValue"]
321
- if "intValue" in value_obj:
322
- return int(value_obj["intValue"])
323
- if "doubleValue" in value_obj:
324
- return float(value_obj["doubleValue"])
325
- if "boolValue" in value_obj:
326
- return value_obj["boolValue"]
327
- if "kvlistValue" in value_obj:
328
- kv = value_obj["kvlistValue"]
329
- return {item.get("key", ""): _parse_otlp_any_value(item.get("value", {})) for item in kv.get("values", [])}
330
- if "arrayValue" in value_obj:
331
- arr = value_obj["arrayValue"]
332
- return [_parse_otlp_any_value(v) for v in arr.get("values", [])]
333
- if "bytesValue" in value_obj:
334
- return value_obj["bytesValue"]
335
- return value_obj
336
-
337
-
338
319
  def _parse_otlp_body(body_raw: dict) -> dict | str:
339
320
  """Parse OTLP log record body value.
340
321
 
341
322
  Top-level stringValue bodies are JSON-decoded (Strands-style logs store
342
323
  message content as JSON strings). All other AnyValue types are parsed
343
- recursively via ``_parse_otlp_any_value`` (handles the nested kvlistValue /
324
+ recursively via ``decode_any_value`` (handles the nested kvlistValue /
344
325
  arrayValue structures used by the OpenAI instrumentor).
345
326
  """
346
327
  if "stringValue" in body_raw:
@@ -351,4 +332,4 @@ def _parse_otlp_body(body_raw: dict) -> dict | str:
351
332
  return json.loads(raw)
352
333
  except (json.JSONDecodeError, TypeError):
353
334
  return raw
354
- return _parse_otlp_any_value(body_raw)
335
+ return decode_any_value(body_raw)
@@ -17,6 +17,7 @@ import re
17
17
  from typing import Any, Protocol, TypedDict, TypeVar
18
18
 
19
19
  from .loader.base import Span, Trace
20
+ from .otlp_anyvalue import decode_attributes
20
21
  from .trace_attrs import (
21
22
  ADK_LLM_REQUEST,
22
23
  ADK_LLM_RESPONSE,
@@ -523,20 +524,12 @@ def is_invocation_span(span: Span) -> bool:
523
524
 
524
525
 
525
526
  def flatten_otlp_attributes(attrs_list: list[dict]) -> dict[str, Any]:
526
- """Convert OTLP attributes array [{key, value: {stringValue|...}}] to flat dict."""
527
- result: dict[str, Any] = {}
528
- for attr in attrs_list:
529
- key = attr.get("key", "")
530
- value_obj = attr.get("value", {})
531
- if "stringValue" in value_obj:
532
- result[key] = value_obj["stringValue"]
533
- elif "intValue" in value_obj:
534
- result[key] = int(value_obj["intValue"])
535
- elif "doubleValue" in value_obj:
536
- result[key] = float(value_obj["doubleValue"])
537
- elif "boolValue" in value_obj:
538
- result[key] = value_obj["boolValue"]
539
- return result
527
+ """Convert OTLP attributes array [{key, value: {stringValue|...}}] to flat dict.
528
+
529
+ Delegates to the shared ``AnyValue`` decoder so array/kvlist/bytes
530
+ attributes survive instead of being dropped.
531
+ """
532
+ return decode_attributes(attrs_list)
540
533
 
541
534
 
542
535
  # ---------------------------------------------------------------------------
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import json
6
6
  import logging
7
7
 
8
+ from ..otlp_anyvalue import decode_attribute, decode_attributes, is_any_value
8
9
  from ..trace_attrs import (
9
10
  OTEL_GENAI_INPUT_MESSAGES,
10
11
  OTEL_GENAI_OUTPUT_MESSAGES,
@@ -175,8 +176,10 @@ class OtlpJsonLoader(TraceLoader):
175
176
  key = attr.get("key", "")
176
177
  if key in self._GENAI_EVENT_KEYS and key not in attributes:
177
178
  value_obj = attr.get("value", {})
178
- if "stringValue" in value_obj:
179
- attributes[key] = value_obj["stringValue"]
179
+ if is_any_value(value_obj):
180
+ keep, value = decode_attribute(key, value_obj)
181
+ if keep:
182
+ attributes[key] = value
180
183
 
181
184
  def _extract_attributes(self, attrs) -> dict:
182
185
  """Convert attributes to a flat ``{key: value}`` dict.
@@ -192,25 +195,7 @@ class OtlpJsonLoader(TraceLoader):
192
195
  if isinstance(attrs, dict):
193
196
  return self._flatten_nested_dict(attrs)
194
197
 
195
- result = {}
196
- for attr in attrs:
197
- key = attr.get("key", "")
198
- value_obj = attr.get("value", {})
199
-
200
- if "stringValue" in value_obj:
201
- result[key] = value_obj["stringValue"]
202
- elif "intValue" in value_obj:
203
- result[key] = int(value_obj["intValue"])
204
- elif "doubleValue" in value_obj:
205
- result[key] = float(value_obj["doubleValue"])
206
- elif "boolValue" in value_obj:
207
- result[key] = value_obj["boolValue"]
208
- elif "arrayValue" in value_obj:
209
- result[key] = json.dumps(value_obj["arrayValue"])
210
- elif "kvlistValue" in value_obj:
211
- result[key] = json.dumps(value_obj["kvlistValue"])
212
-
213
- return result
198
+ return decode_attributes(attrs)
214
199
 
215
200
  @staticmethod
216
201
  def _flatten_nested_dict(d: dict, prefix: str = "") -> dict:
@@ -0,0 +1,118 @@
1
+ """Shared decoder for the OTLP ``AnyValue`` union.
2
+
3
+ OTLP encodes every attribute value, log body and nested element as an
4
+ ``AnyValue``: a one-of wrapper such as ``{"stringValue": "chat"}`` or
5
+ ``{"arrayValue": {"values": [...]}}``. The protobuf receiver (via
6
+ ``MessageToDict``) and OTLP/JSON payloads deliver that same dict shape, so
7
+ every consumer needs identical decoding rules.
8
+
9
+ This module depends only on the standard library and ``trace_attrs`` (a leaf
10
+ constants module), so ``extraction``, ``loader.otlp`` and ``api.otlp_processing``
11
+ can all use it without creating an import cycle.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import logging
17
+ from typing import Any
18
+
19
+ from .trace_attrs import SPEC_CONTAINER_ATTRS
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+ ANY_VALUE_FIELDS = (
24
+ "stringValue",
25
+ "intValue",
26
+ "doubleValue",
27
+ "boolValue",
28
+ "kvlistValue",
29
+ "arrayValue",
30
+ "bytesValue",
31
+ )
32
+
33
+
34
+ def decode_any_value(value_obj: dict) -> Any:
35
+ """Recursively decode an OTLP ``AnyValue`` to a native Python value.
36
+
37
+ Handles the full union: stringValue, intValue (OTLP sends it as a
38
+ string), doubleValue, boolValue, kvlistValue (→ dict), arrayValue
39
+ (→ list), bytesValue.
40
+
41
+ ``bytesValue`` is returned unchanged. ``MessageToDict`` base64-encodes
42
+ protobuf bytes fields and OTLP/JSON does the same, so callers already
43
+ receive a str; decoding it here would change the value they see today.
44
+
45
+ A value carrying none of the union fields is returned as-is.
46
+ """
47
+ if "stringValue" in value_obj:
48
+ return value_obj["stringValue"]
49
+ if "intValue" in value_obj:
50
+ return int(value_obj["intValue"])
51
+ if "doubleValue" in value_obj:
52
+ return float(value_obj["doubleValue"])
53
+ if "boolValue" in value_obj:
54
+ return value_obj["boolValue"]
55
+ if "kvlistValue" in value_obj:
56
+ kv = value_obj["kvlistValue"]
57
+ return {item.get("key", ""): decode_any_value(item.get("value", {})) for item in kv.get("values", [])}
58
+ if "arrayValue" in value_obj:
59
+ arr = value_obj["arrayValue"]
60
+ return [decode_any_value(v) for v in arr.get("values", [])]
61
+ if "bytesValue" in value_obj:
62
+ return value_obj["bytesValue"]
63
+ return value_obj
64
+
65
+
66
+ def is_any_value(value_obj: dict) -> bool:
67
+ """Return True when *value_obj* carries one of the ``AnyValue`` fields."""
68
+ for field in ANY_VALUE_FIELDS:
69
+ if field in value_obj:
70
+ return True
71
+ return False
72
+
73
+
74
+ def decode_attribute(key: str, value_obj: dict) -> tuple[bool, Any]:
75
+ """Decode one attribute, applying the container allowlist.
76
+
77
+ Returns ``(keep, value)``. Scalars are always kept. A list or dict is kept
78
+ only when *key* is in :data:`SPEC_CONTAINER_ATTRS`; otherwise it is dropped,
79
+ which is what ``extraction.py`` did with containers before this decoder was
80
+ shared.
81
+
82
+ Dropping rather than serialising is deliberate: JSON-dumping the value would
83
+ put a blob back into user-visible output, which is the symptom #173 is
84
+ about. What the default *should* be is tracked in #208.
85
+ """
86
+ value = decode_any_value(value_obj)
87
+ if isinstance(value, (list, dict)) and key not in SPEC_CONTAINER_ATTRS:
88
+ logger.warning(
89
+ "Dropping container value for %s (got %s); only spec container attributes are kept",
90
+ key,
91
+ type(value).__name__,
92
+ )
93
+ return False, None
94
+ return True, value
95
+
96
+
97
+ def decode_attributes(attrs_list: list[dict]) -> dict[str, Any]:
98
+ """Decode an OTLP attributes array to a flat ``{key: value}`` dict.
99
+
100
+ Entries whose value carries no ``AnyValue`` field are skipped, matching the
101
+ behaviour every call site had before they shared this decoder.
102
+
103
+ Container values survive only for the keys in
104
+ :data:`~agentevals.trace_attrs.SPEC_CONTAINER_ATTRS`. Keeping the allowlist
105
+ here rather than narrowing per consumer means an attribute nobody has
106
+ thought about cannot become an unhashable dict key downstream - there is
107
+ nothing to remember, because it was never widened in the first place.
108
+ """
109
+ result: dict[str, Any] = {}
110
+ for attr in attrs_list:
111
+ value_obj = attr.get("value", {})
112
+ if not is_any_value(value_obj):
113
+ continue
114
+ key = attr.get("key", "")
115
+ keep, value = decode_attribute(key, value_obj)
116
+ if keep:
117
+ result[key] = value
118
+ return result
@@ -84,3 +84,33 @@ GENAI_ATTRIBUTE_ALIASES: dict[str, list[str]] = {
84
84
 
85
85
  # agentevals custom attributes (repository-specific, outside OTel semconv)
86
86
  AGENTEVALS_SESSION_ID = "agentevals.session_id"
87
+ AGENTEVALS_EVAL_SET_ID = "agentevals.eval_set_id"
88
+ AGENTEVALS_SESSION_NAME = "agentevals.session_name"
89
+
90
+ # Attributes the GenAI semconv types as arrays or structured values. These are
91
+ # the only keys the shared AnyValue decoder returns as a native list or dict;
92
+ # every other key is decoded to a scalar or dropped, which is what
93
+ # ``extraction.py`` did before the decoder was shared.
94
+ #
95
+ # The set is an allowlist on purpose. A missing entry drops one value, matching
96
+ # the pre-existing extraction behaviour; a denylist would instead let an
97
+ # unhashable value reach a dict key or set member on an unauthenticated
98
+ # receiver port, which crashes ingestion. See the discussion on #187.
99
+ #
100
+ # Hand-maintained: opentelemetry-semantic-conventions exposes constants and
101
+ # prose docstrings only, so attribute value types are not machine-readable and
102
+ # this set cannot be derived from the package.
103
+ #
104
+ # What should happen to container values on keys outside this set is not
105
+ # settled; today they are dropped. Tracked in #208.
106
+ SPEC_CONTAINER_ATTRS: frozenset[str] = frozenset(
107
+ {
108
+ OTEL_GENAI_RESPONSE_FINISH_REASONS,
109
+ OTEL_GENAI_INPUT_MESSAGES,
110
+ OTEL_GENAI_OUTPUT_MESSAGES,
111
+ OTEL_GENAI_TOOL_DEFINITIONS,
112
+ OTEL_GENAI_SYSTEM_INSTRUCTIONS,
113
+ OTEL_GENAI_TOOL_CALL_ARGUMENTS,
114
+ OTEL_GENAI_TOOL_CALL_RESULT,
115
+ }
116
+ )
@@ -6,6 +6,7 @@ import json
6
6
 
7
7
  import pytest
8
8
 
9
+ from agentevals import trace_attrs
9
10
  from agentevals.extraction import (
10
11
  AdkExtractor,
11
12
  GenAIExtractor,
@@ -400,6 +401,129 @@ class TestFlattenOtlpAttributes:
400
401
  )
401
402
  assert result == {"str": "hello", "num": 3.14, "flag": True}
402
403
 
404
+ def test_array_value(self):
405
+ result = flatten_otlp_attributes(
406
+ [
407
+ {
408
+ "key": "gen_ai.response.finish_reasons",
409
+ "value": {"arrayValue": {"values": [{"stringValue": "stop"}]}},
410
+ },
411
+ ]
412
+ )
413
+ assert result == {"gen_ai.response.finish_reasons": ["stop"]}
414
+
415
+ def test_kvlist_value(self):
416
+ result = flatten_otlp_attributes(
417
+ [
418
+ {
419
+ "key": "gen_ai.tool.call.arguments",
420
+ "value": {
421
+ "kvlistValue": {
422
+ "values": [
423
+ {"key": "city", "value": {"stringValue": "Berlin"}},
424
+ {"key": "metric", "value": {"boolValue": False}},
425
+ ]
426
+ }
427
+ },
428
+ },
429
+ ]
430
+ )
431
+ assert result == {"gen_ai.tool.call.arguments": {"city": "Berlin", "metric": False}}
432
+
433
+ def test_array_of_kvlist(self):
434
+ """Messages arrive as an arrayValue of kvlistValue."""
435
+ result = flatten_otlp_attributes(
436
+ [
437
+ {
438
+ "key": "gen_ai.input.messages",
439
+ "value": {
440
+ "arrayValue": {
441
+ "values": [
442
+ {"kvlistValue": {"values": [{"key": "role", "value": {"stringValue": "user"}}]}},
443
+ ]
444
+ }
445
+ },
446
+ },
447
+ ]
448
+ )
449
+ assert result == {"gen_ai.input.messages": [{"role": "user"}]}
450
+
451
+ def test_finish_reasons_survive_to_extracted_model_info(self):
452
+ """The symptom #173 names: gen_ai.response.finish_reasons reaching the
453
+ consumer as ["stop"] rather than a literal blob or nothing.
454
+
455
+ Asserting through extract_extended_model_info_from_attrs rather than at
456
+ the decoder keeps the whole path covered - decoding it correctly is not
457
+ the same as it arriving correctly.
458
+ """
459
+ attrs = flatten_otlp_attributes(
460
+ [
461
+ {
462
+ "key": "gen_ai.response.finish_reasons",
463
+ "value": {"arrayValue": {"values": [{"stringValue": "stop"}]}},
464
+ },
465
+ {"key": "gen_ai.response.model", "value": {"stringValue": "claude-opus-5"}},
466
+ ]
467
+ )
468
+ info = extract_extended_model_info_from_attrs(attrs)
469
+ assert info["finish_reasons"] == ["stop"]
470
+ assert info["response_model"] == "claude-opus-5"
471
+
472
+ def test_multiple_finish_reasons_survive(self):
473
+ attrs = flatten_otlp_attributes(
474
+ [
475
+ {
476
+ "key": "gen_ai.response.finish_reasons",
477
+ "value": {"arrayValue": {"values": [{"stringValue": "stop"}, {"stringValue": "length"}]}},
478
+ }
479
+ ]
480
+ )
481
+ assert extract_extended_model_info_from_attrs(attrs)["finish_reasons"] == [
482
+ "stop",
483
+ "length",
484
+ ]
485
+
486
+ def test_unlisted_key_drops_container_value(self):
487
+ """Containers survive only for SPEC_CONTAINER_ATTRS. Everything else is
488
+ dropped, which is what extraction did before the decoder was shared."""
489
+ attrs = flatten_otlp_attributes(
490
+ [
491
+ {
492
+ "key": "gen_ai.response.model",
493
+ "value": {"arrayValue": {"values": [{"stringValue": "claude-opus-5"}]}},
494
+ },
495
+ {"key": "gen_ai.request.model", "value": {"stringValue": "claude-sonnet-5"}},
496
+ ]
497
+ )
498
+ assert "gen_ai.response.model" not in attrs
499
+ info = extract_extended_model_info_from_attrs(attrs)
500
+ assert info["response_model"] is None
501
+ assert info["request_model"] == "claude-sonnet-5"
502
+
503
+ def test_no_unlisted_key_can_yield_an_unhashable_value(self):
504
+ """The property the allowlist exists for: nothing outside
505
+ SPEC_CONTAINER_ATTRS can reach a consumer as a dict key or set member
506
+ and raise TypeError. Covers every attribute constant we declare, so a
507
+ new one cannot quietly reopen the hazard."""
508
+ container = {"arrayValue": {"values": [{"stringValue": "x"}]}}
509
+ for name in dir(trace_attrs):
510
+ if not name.isupper():
511
+ continue
512
+ key = getattr(trace_attrs, name)
513
+ if not isinstance(key, str):
514
+ continue
515
+ value = flatten_otlp_attributes([{"key": key, "value": container}]).get(key)
516
+ if key in trace_attrs.SPEC_CONTAINER_ATTRS:
517
+ assert value == ["x"], f"{key} should keep its container"
518
+ else:
519
+ assert value is None, f"{key} leaked a container"
520
+ hash(value)
521
+
522
+ def test_bytes_value(self):
523
+ """MessageToDict base64-encodes bytes fields, so the decoder sees a str."""
524
+ result = flatten_otlp_attributes([{"key": "payload", "value": {"bytesValue": "AP9oaQ=="}}])
525
+ assert result == {"payload": "AP9oaQ=="}
526
+
403
527
  def test_empty(self):
404
528
  assert flatten_otlp_attributes([]) == {}
405
529