Skip to content

feat(ag2-classic): traceAI-ag2-classic for autogen 0.14.x (TH-8237) - #211

Merged
nik13 merged 23 commits into
devfrom
feat/th-8237-ag2-classic
Oct 26, 2026
Merged

nik13 merged 23 commits into
devfrom
feat/th-8237-ag2-classic

Conversation

@nik13

@nik13 nik13 commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Summary

New Python package traceAI-ag2-classic for AG2 Classic (TH-8237): the autogen 0.14.x distribution from ag2ai/ag2classic @ 86fa5ccb. It is a native-first enabler on autogen.opentelemetry plus an AG2ClassicSpanProcessor. It depends on autogen>=0.14.0,<0.15 only; there is no dependency on ag2, pyautogen or autogen-agentchat.

The provisional mapping cells from the architecture were replaced with what the installed source actually emits (paths under autogen/opentelemetry/):

  • Kinds: the span kind is the custom key ag2.span.type (llm_wrapper.py:74, reply.py:36, chat.py:33, tool.py:26, pattern.py:163). It maps chat to LLM, invoke_agent to AGENT, execute_tool to TOOL, and conversation and speaker_selection to CHAIN.
  • Usage and cost: usage is emitted (llm_wrapper.py:121-125), so no generate_reply wrapper is needed. Cost is emitted too (gen_ai.usage.cost), and is copied to gen_ai.cost.total on LLM spans only.
  • Session: session.id comes from gen_ai.conversation.id, on the outermost conversation only. Nested speaker-selection chats would otherwise split the trace into several sessions.
  • Token double-count fixed: conversation spans put the chat-wide aggregate_usage sum on gen_ai.usage.* (chat.py:81-82, 146-147). fi-collector promotes those keys on any span, and Observe sums tokens over a trace. Now every gen_ai.usage.* key, cost included, stays on LLM spans only; the conversation aggregate moves to ag2.usage.*. The README notes how cost behaves for cached responses.
  • Content: the upstream flag only covers the LLM wrapper. Agent, conversation, tool, human-input and code spans always record content (reply.py:51,58; tool.py:36,47; ...). The processor drops those keys unless capture_content=True.
  • Errors: upstream catches tool exceptions and only sets error.type. The processor sets ERROR status from it and runs first, ahead of fi's BatchSpanProcessor, which would otherwise turn UNSET into OK.

Decisions (approved: Nikhil 2026-10-03 blanket)

  • Version guard: PyPI autogen==0.14.0 is an alias that installs ag2==0.14.0, and that package ships the top-level autogen module with an identical opentelemetry tree. The guard accepts exactly the autogen 0.14.x + ag2 0.14.x pair and rejects every other ag2 owner, including 1.x.
  • setup() signature: setup(agents=..., patterns=...) returns a handle with instrument_agent, instrument_pattern, instrument_a2a_server (opt-in) and uninstrument. Upstream instruments objects one at a time (agent.py:27, pattern.py:23).

Tests (run on 2f1a5fc, clean tree, by the coordinator)

PYTHONPATH="python/frameworks/ag2-classic:python:python/tests" uv run --no-project --python 3.11 --with pytest --with opentelemetry-api --with opentelemetry-sdk --with opentelemetry-exporter-otlp-proto-http --with wrapt --with requests --with jsonschema --with "autogen[openai]==<v>" pytest python/frameworks/ag2-classic/tests python/tests/harness/test_harness.py -q -p no:cacheprovider --noconftest -o addopts= -rs
autogen 0.14.1: 90 passed
autogen 0.14.0: 90 passed

The implementer also ran autogen 0.14.1 on Python 3.10 and 3.13 (90 passed each). The documented install was smoke-tested in clean venvs. The old line fails with ImportError ... 'openai' is not installed; the new "autogen[openai]" line runs the quick start. No files changed outside the package.

Limits

  • No live vendor call, and no real fi-collector.
  • Code execution, human input, initiate_chats, remote agents and the A2A server are checked by mapping only.
  • A classic GroupChatManager emits no conversation span of its own upstream.
  • LLM spans bind to the first provider that is set up (upstream patches OpenAIWrapper.create once).
  • The docs draft in company-brain PR Dev #62 still has the provisional cells; the README tables here are the checked versions.

Review

Round 1, an independent fresh-context review of 96ef5b3 (a subagent, not an installed card): CHANGES_REQUESTED. Fixed test first. All final tests run against 96ef5b3 give 22 failed / 59 passed (assertion or KeyError failures only).

ID Finding Resolution Commit
R1 uninstrument() left content dropping off Content dropping kept after uninstrument 10e9dd7
R2 A later add_span_processor removed the processor Kept first across the call 911cabf
R3 A user span split one trace into two sessions Non-AG2 spans followed to the outer conversation; tracking bounded 5805ca0
R4 using_attributes ignored on AG2 spans Copied on start, without overwriting upstream values a383f20
R5 initiate_chats sessions using_session gives one session; the default (one per chat) is pinned by a test a383f20
R6 setup() not atomic The LLM patch is rolled back when an agent fails e4540e4
Contract No usage/cost keys on non-LLM spans Every gen_ai.usage.* key off non-LLM spans (13 failed before) cb44a2f
Missing test LLM exception path Real-autogen HTTP 400 test plus contract assertions 54a587c
Nit _guard.py alias description Corrected (the 0.14.0 wheel is 6 metadata entries) af1845a

Round 2: installed pr-reviewer, then pr-verifier, on af1845a. Verdicts will be added here.

Round 2 follow-ups (af1845a..2f1a5fc)

The installed review passed this PR at af1845a: pr-reviewer t_7aba4044 APPROVE, pr-verifier t_52edde89 VERIFIED. Its findings were fixed before the demo. Run against af1845a, the new tests give 3 failed and 87 passed (the N4, N5 and N6 tests).

ID Finding Resolution
N1 The documented install has no openai, so the quick start fails Install is pip install traceAI-ag2-classic "autogen[openai]" (0af3c23)
N2 Live-span tracking caps at 10000 README Limits (2f1a5fc)
N3 Session wording overstated README Session row reworded; assertions added (9c2d57d)
N4 Re-setup reused a disabled processor, which fails open setup() reopens it (2aee53b)
N5 ConcurrentMultiSpanProcessor ordering is not guaranteed WARNING logged (9df5068)
N6 uninstrument() restored create over a later wrapper Restores only while ours is installed (6410498)

Round 3 used installed pr-verifier t_7cf46910 at 2f1a5fc and returned VERIFIED, ready to merge. N1 and N4-N6 are fixed and N2/N3 are documented, with no regressions. The delta does not touch _processor.py or the mapping. The new setup code can only re-enable filtering (N4), add a log line (N5) or skip a restore (N6).

Review is complete at 2f1a5fc. Two P3 follow-ups, left for after merge:

  • F1: the README says to call setup() again after hand-editing _span_processors. setup() only prepends the processor when it is missing, so an edit that leaves it present but not first keeps that order. Either always prepend in the reuse branch (it is idempotent) or reword the README.
  • F2: spans that started while the processor was disabled and end after setup() reopens it are redacted, but they lack using_* context attributes. Only spans that straddle the reopen are affected.

Video demo

Narrated terminal demo, 6:14, recorded at head 2f1a5fc (the reviewed head), 1080p H.264/AAC, 9 chapters. sha256 9ad20da7e548336ff206cbbee2384701681eb3508f1d60f882c8f5f31b624b2c.

The video, captions, transcript, chapter list, preview and media check are attached privately to Linear TH-8237 (Future AGI workspace access needed). Every run uses real autogen 0.14.1 with a loopback fake OpenAI-compatible model, placeholder keys and the shared harness receiver: no vendor call and no live fi-collector.

Time Chapter
00:00 The problem: AG2 classic spans before this package
00:43 Head 2f1a5fc: branch, 0 uncommitted files, 16 commits on 3eaadc8
01:19 Chat: real two-agent initiate_chat with tool get_weather; 7 spans in 1 trace, 1 POST to /tracer/v1/traces with both auth headers (values not shown); kinds CHAIN / AGENT / LLM / TOOL; promoted tokens input 22 / total 36 counted once (conversation aggregate kept as ag2.usage.input_tokens); no content keys and 0 prompt or tool-argument occurrences by default
02:28 Session: using_session carries session.id on all 7 spans
02:56 Reopen before/after (review N4): at af1845a, no kinds, input tokens 44 for 2 x 11 and content keys exported; at the head, LLM/CHAIN kinds, 22 counted once, no content
03:46 Guards: concurrent span processors warning and a later third-party wrapper kept on uninstrument
04:20 The change: N4, N5, N6, N1
05:05 Tests: the three round-2 tests PASSED, then 90 passed on camera, 0 uncommitted files after
05:38 Review and limits

Disclosed cuts remove frozen waits only (the 90-test run took 146.72 s under recording load, shown on screen). Checked by the coordinator: sha256, full decode, chapter markers, one 3.1 s pause at the first chapter boundary and none inside a chapter, frames per chapter compared with the driver output, no keys or header values in frames, captions or transcript.

Stacked on #203. Not merged.

Linear: TH-8237. approved: Nikhil 2026-10-03 blanket

Verification sweep (2026-10-12)

Re-verification (2026-10-16)

Result: one fix pushed (content default), everything else confirmed. Commit 695f8b8 on this branch (new commit, no force push).

Versions. Latest stable upstream autogen 0.14.1 (PyPI, 2026-10-16; ag2ai/ag2classic has no commit since 2026-07-08) and the pinned floor 0.14.0 (alias for ag2==0.14.0). fi-instrumentation-otel 1.1.0, Python 3.11. Model: CLIProxy gpt-6-luna (OpenAI-compatible, real calls). Target: local Future AGI (th8103-e2e), project th8103-rick-ag2-classic.

What was wrong. The package used a package-specific switch, setup(capture_content=False) default, so content was off by default. Every traceAI instrumentor follows TraceConfig (content on by default, hidden with FI_HIDE_INPUTS / FI_HIDE_OUTPUTS and the message/text variants).

Fix. setup(config=TraceConfig | None, capture_content: bool | None = None): default reads TraceConfig() (env FI_HIDE_*); capture_content=True/False stays as an optional override. The processor hides the input side (gen_ai.input.messages, tool arguments, human prompt, input.value) and the output side (gen_ai.output.messages, tool result, code output, chat summaries, output.value) independently. README, CHANGELOG, example and 8 new/updated tests follow. Docs page: future-agi/docs#993 (draft, base main, no navigation.ts).

Tests. python/frameworks/ag2-classic/tests + python/tests/harness: 105 passed on autogen 0.14.1 and 105 passed on 0.14.0 (Python 3.11).

Real E2E (installed as a customer would: pip install -e of the package + autogen[openai]; tool chat user / assistant with get_weather):

Run Trace id Result
0.14.1 default, success + tool aab00f16-b6ee-4840-be04-d8819f076e6e 7 spans, one session
0.14.0 default, success + tool 848844bb-8d00-4177-a4c0-f937db678590 7 spans
0.14.1 tool raises 0cd2f18d-135e-4a9f-a18a-c29a78352ebb tool span ERROR:ExecutionError
0.14.1 model call fails (unknown model, HTTP 400) af156619-d295-4508-924f-f7fa2e5c1f48 conversation, agent and llm spans ERROR:BadRequestError
0.14.1 FI_HIDE_INPUTS=true FI_HIDE_OUTPUTS=true f2c0921f-b005-4dfb-82ab-c89643c792c0 content absent
hidden + tool raises / model fails efce4992-a629-4719-83d8-06ecdfa0eb15 / 71eb69bc-9b84-4519-84db-2b8830e396c0 ERROR kept, marker absent

fi_verify (PASS, --expect-type llm tool agent), trace aab00f16:

- [chain] conversation user model=gpt-6-luna in=107B out=459B
  - [agent] invoke_agent assistant model=gpt-6-luna
    - [llm] chat gpt-6-luna model=gpt-6-luna tok=369/35
  - [agent] invoke_agent user
    - [tool] execute_tool get_weather in=16B out=34B
  - [agent] invoke_agent assistant model=gpt-6-luna
    - [llm] chat gpt-6-luna model=gpt-6-luna tok=410/27

Tokens sit on the two LLM spans only (counted once); session.id is set; the UI trace view shows the tree and agent graph correctly.

Privacy proof. A unique marker (MK<run><n>) was put in the prompt, the tool result and the tool exception message, and every stored ClickHouse spans column (attributes, resource, events) was scanned. Defaults: marker on 7 of 7 spans. Both hide variables set: marker on 0 spans, for the success run, the tool-fails run and the model-fails run. No /Users, user name, host name or Traceback in any stored span in either mode. Known: provider and tool exception messages appear in span status text (standard OpenTelemetry), noted in the README.

Limits unchanged. Code-execution, human-input, remote-agent and A2A spans are mapping-tested only; retrieval and a GroupChatManager conversation span are not emitted upstream.

nik13 added 5 commits October 3, 2026 18:58
The Node OTLP exporter streams with Transfer-Encoding: chunked and sends
no Content-Length. The Receiver read Content-Length only, so every Node
export came back 400 with zero spans; two TH-8103 children had to put a
de-chunking relay in front of it. Receiver now accepts both framings.

It also records one entry per accepted export (path, lower-cased
headers, flattened resource attributes) via requests(), so a contract
test can assert X-Api-Key/X-Secret-Key and project_name through the
shared harness instead of a private recorder.

Verified: 6 harness tests pass, and the TanStack example's real Node
exporter delivered 4 chunked exports (4 spans, collector path, both
auth headers, project_name/project_type) with no relay.

Refs: TH-8339, TH-8103
… 0.14.x

New package python/frameworks/ag2-classic (traceAI-ag2-classic). setup()
enables AG2 Classic's built-in autogen.opentelemetry (instrument_llm_wrapper,
instrument_agent, instrument_pattern) on the Future AGI tracer provider from
register(); it never creates a provider, sets the global provider or adds an
exporter.

AG2ClassicSpanProcessor is prepended ahead of the exporting processor and,
from keys read in autogen/opentelemetry/instrumentators at 0.14.1:
- maps ag2.span.type to gen_ai.span.kind (handoff -> CHAIN, never emitted)
- copies the outermost gen_ai.conversation.id to session.id
- adds gen_ai.cost.total (from gen_ai.usage.cost) and
  gen_ai.usage.total_tokens on LLM spans
- sets ERROR status from upstream error.type (tool failures are caught
  inside execute_function, so upstream never sets status)
- drops message/tool/human-input/code-output content unless
  capture_content=True (upstream has no flag outside the LLM wrapper)

Version guard rejects non-0.14 autogen, Microsoft AutoGen, ag2 1.x and
pyautogen layouts, and accepts the autogen==0.14.0 alias backed by
ag2==0.14.0. Tests: mapping and guard unit tests, in-process real autogen
runs against a loopback fake OpenAI server, and a shared-harness contract
test over the real OTLP/HTTP exporter.

Refs: TH-8237
Conversation spans carry the chat-wide aggregate_usage sum as
gen_ai.usage.input_tokens/output_tokens (autogen 0.14.1
opentelemetry/instrumentators/agent_instrumentators/chat.py 81-82,
146-147) on top of the per-call tokens on the LLM spans
(llm_wrapper.py 123-125). fi-collector promotes those keys on any span
(adapter.go) and Observe sums total_tokens over a trace, so totals
doubled. Non-LLM spans now keep the values under ag2.usage.*.

Tests first: new processor test plus a trace-sum assertion in the
two-agent setup test. 59 passed on autogen 0.14.1 and 0.14.0.

approved: Nikhil 2026-10-03 blanket
Refs: TH-8237
nik13 added 14 commits October 4, 2026 16:45
uninstrument() removed AG2ClassicSpanProcessor and shut it down, but
upstream instrument_agent patches each agent once with no undo. Agents
instrumented earlier kept emitting spans on the same provider, now
unfiltered: gen_ai.input/output.messages were exported on invoke_agent and
conversation spans, and conversation spans regained the chat-wide
gen_ai.usage.* tokens (double count). The same happened to a second handle
sharing the processor when the first handle uninstrumented.

uninstrument() now restores OpenAIWrapper.create only. The processor stays
installed, shared by every handle on the provider, and shuts down with the
provider.

approved: Nikhil 2026-10-03 blanket
Refs: TH-8237
setup() installed the processor and patched OpenAIWrapper.create before
instrumenting agents and patterns. If one of those raised, the caller never
got the handle and the global patch could not be undone. setup() now
restores OpenAIWrapper.create and re-raises; the processor stays, because
agents instrumented before the failure keep emitting spans.

approved: Nikhil 2026-10-03 blanket
Refs: TH-8237
Future AGI's TracerProvider.add_span_processor shuts down and clears every
processor on the first call after register() (fi_instrumentation/otel.py
336-339), including AG2ClassicSpanProcessor. Content then leaked to the new
processor and conversation spans kept the promoted gen_ai.usage.* tokens.

setup() now wraps add_span_processor on the provider instance once. After
the original runs, our processor is re-enabled if that call shut it down
and re-prepended ahead of every other processor. setup() finds the
processor through the wrapper, so repeated setup() calls never stack a
second guard or processor. README Privacy and Troubleshooting updated.

approved: Nikhil 2026-10-03 blanket
Refs: TH-8237
…ation

The processor tracked only AG2-scope spans and stopped the ancestor walk at
the first untracked parent. A user span between two AG2 conversations (an
inner initiate_chat inside the user's own span, agent-as-tool style) hid
the outer conversation, so the inner chat id became a second session.id in
the same trace.

on_start now records (parent span id, AG2 span or None) for every live span
on the provider, still bounded by _MAX_LIVE_SPANS, and on_end releases every
span. The walk continues through non-AG2 spans.

approved: Nikhil 2026-10-03 blanket
Refs: TH-8237
Upstream spans come from a plain OpenTelemetry tracer; only Future AGI's
own tracer applies context attributes (fi_instrumentation/instrumentation/
_tracers.py:165). using_session / using_user / using_attributes were
therefore ignored: session.id was the chat id and user.id was absent.

on_start now copies get_attributes_from_context() (read from the span's
parent context) onto AG2 spans, without overwriting keys upstream already
set. A context session.id is on the span before it ends, so it wins over
the gen_ai.conversation.id copy; the conversation span keeps the real chat
id in gen_ai.conversation.id.

initiate_chats keeps one session per outermost conversation and no session
on the multi_conversation parent. The README Session row now says so and
points to using_session(...) for one session over the whole call; tests
cover both.

approved: Nikhil 2026-10-03 blanket
Refs: TH-8237
The conversation span still carried upstream's chat-wide gen_ai.usage.cost
after the token move. The contract test now asserts, on the OTLP the shared
Receiver decoded, that no gen_ai.usage.* / gen_ai.cost.total /
llm.cost.total key appears on a non-LLM span and that each trace's
gen_ai.usage.total_tokens sum equals its LLM calls x 18 (the reviewer's M1
and M5 mutations passed the contract test before).

map_ag2_attributes now moves every gen_ai.usage.* key on a non-LLM span to
ag2.usage.*, so the aggregate cost lands in ag2.usage.cost next to the
aggregate tokens. README Cost row updated, and notes that upstream reports
response.cost on cache hits (oai/client.py:1230-1241), so cached replies
over-report cost (upstream behaviour, not fixed here).

approved: Nikhil 2026-10-03 blanket
Refs: TH-8237
A model call that raises was untested. The loopback fake model now answers
HTTP 400 to a request carrying LLM_FAIL_TRIGGER (OpenAI raises
BadRequestError, no retry), and failing_llm_chat runs a chat whose only
model call is rejected.

- test_setup.py: the LLM span keeps status ERROR, carries
  error.type=BadRequestError and gen_ai.span.kind=LLM, has no usage or cost,
  and no content leaks on the error path.
- Contract test: the same through register()'s exporting processor (which
  turns only UNSET into OK) and the shared Receiver; the token-sum check
  counts successful LLM spans only.

Upstream sets error.type and re-raises (llm_wrapper.py:97-101) and the SDK
sets ERROR, so these pass on the pre-fix code by design; README Errors row
documents the behaviour.

approved: Nikhil 2026-10-03 blanket
Refs: TH-8237
_guard.py called the autogen==0.14.0 wheel an "8-file alias". The PyPI
wheel has 6 entries, all under autogen-0.14.0.dist-info/ (LICENSE,
METADATA, NOTICE.md, WHEEL, top_level.txt, RECORD), and ships no modules.
Docstring only; no behaviour change.

approved: Nikhil 2026-10-03 blanket
Refs: TH-8237
setup() on a provider that already has AG2ClassicSpanProcessor reused it
and re-prepended it, but never undid a shutdown that bypassed the
add_span_processor guard (an unbound TracerProvider.add_span_processor,
or the multi-processor's shutdown()). A disabled processor returns early
in on_end, so spans were exported unmapped and unfiltered (fail-open).
Reusing the processor now reopens it.

Test: test_setup_again_reopens_a_processor_that_was_shut_down fails
before the fix (LLM span exported with no gen_ai.span.kind and raw
gen_ai.usage.* keys) and passes after.

Review finding N4 (installed review t_7aba4044).

approved: Nikhil 2026-10-03 blanket
Refs: TH-8237
…ently

Content filtering relies on AG2ClassicSpanProcessor running before the
exporting processor. The SDK's ConcurrentMultiSpanProcessor runs every
on_end in parallel on a thread pool, so that order is not guaranteed and
an exporter can serialize a span before content is removed. setup() now
logs a WARNING when the provider's active processor is a
ConcurrentMultiSpanProcessor; the default SynchronousMultiSpanProcessor
(what fi_instrumentation.register() uses) is unchanged and silent.

Test: test_setup_warns_when_span_processors_run_concurrently fails
before the fix (no warning record: assert 0 == 1) and passes after.

Review finding N5 (installed review t_7aba4044).

approved: Nikhil 2026-10-03 blanket
Refs: TH-8237
…rument

uninstrument() restored the captured original OpenAIWrapper.create
unconditionally, so a wrapper another library installed after setup()
was silently dropped. The handle now records the traced function
upstream installed and restores the original only while
OpenAIWrapper.create is still that function; otherwise it logs a
WARNING, leaves the replacement in place and keeps ownership so a later
uninstrument() can still restore. setup()'s rollback path is unchanged
(nothing can replace the method between the patch and the rollback).

Test: test_uninstrument_leaves_a_later_third_party_wrapper_in_place
fails before the fix (OpenAIWrapper.create is the original, not the
third-party wrapper) and passes after.

Review finding N6 (installed review t_7aba4044).

approved: Nikhil 2026-10-03 blanket
Refs: TH-8237
The documented install (`pip install "autogen>=0.14.0,<0.15"` then
`pip install traceAI-ag2-classic`) has no model client: autogen's base
requirements do not include openai, so the README quick start raised
ImportError ('openai' is not installed) when it created an agent with an
llm_config. Install and the example docstring now use
`pip install traceAI-ag2-classic "autogen[openai]"`; the package itself
pins autogen to >=0.14.0,<0.15, and the 0.14.0 alias forwards the extra
to ag2[openai]==0.14.0.

Evidence (scratch venvs, Python 3.11, quick start up to setup(), no
model call): old two-line install -> ImportError from
create_openai_client; new command -> QUICKSTART_OK on autogen 0.14.1
with openai 2.54.0.

Review finding N1 (installed review t_7aba4044).

approved: Nikhil 2026-10-03 blanket
Refs: TH-8237
The Session row said gen_ai.conversation.id "keeps the chat id" inside
using_session / using_attributes. That holds only for the conversation
spans initiate_chat / a_initiate_chat start (upstream sets the key after
start, chat.py:65,131). fi's using_session also puts the session id in
context under gen_ai.conversation.id, and the processor copies missing
context keys at on_start, so LLM, agent, tool, initiate_chats and resumed
conversation spans carry the session id there. README reworded.

Tests pin the documented behaviour (unit: chat m span; real autogen run:
every non-conversation span). They pass before and after by design (doc
fix, no code change); a mutation that skips copying
gen_ai.conversation.id from context makes both fail with KeyError.

Review finding N3 (installed review t_7aba4044).

approved: Nikhil 2026-10-03 blanket
Refs: TH-8237
The nested-conversation check tracks every live span on the provider and
stops tracking at _MAX_LIVE_SPANS (10000); entries are freed in on_end.
A README Limits section now says so, and that the nested-session split
returns only if that many spans leak or run concurrently on the
provider. Upstream ends every span it starts (each instrumentator uses
`with tracer.start_as_current_span`, checked in autogen 0.14.1), so a
leak needs other code on the same provider. Docs only; no code change.

Review finding N2 (installed review t_7aba4044).

approved: Nikhil 2026-10-03 blanket
Refs: TH-8237
@nik13
nik13 requested a review from NVJKKartik October 10, 2026 08:15
nik13 and others added 2 commits October 11, 2026 20:01
AG2 reports cost 0 for any model missing from its price table ("Model ...
is not found. The cost will be 0."). The processor copied that 0 to
gen_ai.cost.total, and Future AGI takes gen_ai.cost.total as the cost and
skips its own token pricing and the org's custom model prices. A real run
with claude-sonnet-5 through an OpenAI-compatible endpoint stored cost 0 on
every LLM span; without the 0 Future AGI prices those calls (0.0014 each).

Copy the upstream cost only when it is above 0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
fi-instrumentation-otel 1.0.0's register() raises AttributeError
('HTTPSpanExporter' object has no attribute '_headers') with
opentelemetry-exporter-otlp-proto-http 1.45, so a fresh install that
resolves 1.0.0 crashes on startup. Use the floor the other TH-8103
packages already declare.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
nik13 and others added 2 commits October 16, 2026 17:54
Content (messages, tool arguments/results, human input, code output) is now
exported by default and hidden with FI_HIDE_INPUTS / FI_HIDE_OUTPUTS (and the
message/text variants) or setup(config=TraceConfig(...)), like every traceAI
instrumentor. capture_content=True/False stays as an optional override. The
processor hides the input and output sides independently.

Re-verified 2026-10-16 end to end on autogen 0.14.1 and 0.14.0 against a local
Future AGI stack; 105 tests pass on both.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
@nik13
nik13 merged commit 83a54b4 into dev Oct 26, 2026
4 checks passed
@nik13
nik13 deleted the feat/th-8237-ag2-classic branch October 26, 2026 09:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant