Skip to content

[codex] add embedded Langfuse client#3

Merged
senamakel merged 1 commit into
mainfrom
codex/embedded-langfuse-client
Jul 1, 2026
Merged

[codex] add embedded Langfuse client#3
senamakel merged 1 commit into
mainfrom
codex/embedded-langfuse-client

Conversation

@senamakel

@senamakel senamakel commented Jul 1, 2026

Copy link
Copy Markdown
Member

Summary

  • embeds Langfuse support in the default TinyAgents crate surface
  • adds LangfuseClient, LangfuseAuth, and LangfuseTraceConfig exports
  • maps durable AgentObservation records into Langfuse trace/generation/tool/event ingestion batches
  • supports backend proxy mode with bearer auth and direct Langfuse mode with project keys

Validation

  • cargo test langfuse
  • cargo test --features openai langfuse
  • git diff --check

Summary by CodeRabbit

  • New Features

    • Added built-in support for exporting harness observations to Langfuse.
    • Introduced flexible connection options for direct access or proxy-based routing.
    • Exposed new observability types at the crate root for easier integration.
  • Documentation

    • Updated the README with an example showing how to send observations to Langfuse and a brief explanation of connection modes.
  • Chores

    • Made the HTTP client dependency available by default for builds.

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b6b2a519-e9f9-4b30-8928-ee208814ab43

📥 Commits

Reviewing files that changed from the base of the PR and between 8f226f1 and 6f898fb.

📒 Files selected for processing (5)
  • Cargo.toml
  • README.md
  • src/harness/observability/langfuse.rs
  • src/harness/observability/mod.rs
  • src/lib.rs

📝 Walkthrough

Walkthrough

Adds a new Langfuse ingestion exporter module for durable harness observations, including client construction, batch building, and async sending. Exports the new types from observability mod and crate root, updates README docs, and makes reqwest a non-optional dependency while emptying the openai feature list.

Changes

Langfuse export feature

Layer / File(s) Summary
Dependency configuration
Cargo.toml
reqwest becomes a non-optional dependency and the openai feature no longer pulls in dep:reqwest.
Auth and trace config types
src/harness/observability/langfuse.rs
Defines LangfuseAuth (Basic/Bearer) and LangfuseTraceConfig (trace metadata, tags, metadata).
Client construction and ingestion sending
src/harness/observability/langfuse.rs
LangfuseClient supports new, direct, proxy, from_env, endpoint, build_ingestion_batch, and async send_observations, with endpoint normalization and HTTP status handling including 207.
Observation mapping and timestamp helpers
src/harness/observability/langfuse.rs
Maps AgentObservation/AgentEvent into Langfuse batch items, serializes usage, strips nulls, converts Unix ms to ISO-8601, and includes unit tests.
Module and crate exports, docs
src/harness/observability/mod.rs, src/lib.rs, README.md
Re-exports LangfuseAuth, LangfuseClient, LangfuseTraceConfig from the observability module and crate root; README documents usage with a code example.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant LangfuseClient
  participant LangfuseAPI

  Caller->>LangfuseClient: send_observations(trace, observations)
  LangfuseClient->>LangfuseClient: build_ingestion_batch(trace, observations)
  LangfuseClient->>LangfuseAPI: POST ingestion endpoint with auth
  LangfuseAPI-->>LangfuseClient: response (status, body)
  LangfuseClient-->>Caller: parsed Value or error
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Poem

A rabbit hops with data light,
Sending traces into the night,
To Langfuse's door, batch in tow,
Basic or Bearer, off we go!
🐇✨ reqwest now always near,
No more optional, code is clear!

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@senamakel senamakel marked this pull request as ready for review July 1, 2026 01:37
@senamakel senamakel merged commit 7be1905 into main Jul 1, 2026
1 of 2 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6f898fbfa4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +286 to +287
"startTime": timestamp,
"endTime": timestamp,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve real call start times

When a batch contains the normal ModelStarted at t0 followed by ModelCompleted at t1, this creates the Langfuse generation with both startTime and endTime set to the completion timestamp, so Langfuse records zero latency for every model call even though the durable observations include the real start time. Pre-index the started observations by call_id and use that timestamp (and model name) when emitting the generation; the tool branch below has the same zero-duration issue.

Useful? React with 👍 / 👎.

.await
.map_err(|e| TinyAgentsError::Model(format!("Langfuse response read failed: {e}")))?;
let parsed = serde_json::from_str(&body).unwrap_or_else(|_| json!({ "message": body }));
if !status.is_success() && status.as_u16() != 207 {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Report per-event failures in 207 responses

Langfuse's ingestion endpoint returns HTTP 207 with a result body that can include errors for individual rejected events, but this treats every 207 as Ok(parsed). In cases such as an invalid generated id or a Langfuse deployment rejecting trace/observation events, callers of send_observations will see success while some or all telemetry was dropped unless they know to inspect the raw JSON themselves; check the parsed errors array and return an error when it is non-empty.

Useful? React with 👍 / 👎.

senamakel added a commit that referenced this pull request Jul 1, 2026
Implements gap #3 (reasoning channel): providers can stream reasoning
separately from visible text, round-tripping through the existing stream
and AgentEvent::ModelDelta so UIs render it from events alone.

- MessageDelta gains a reasoning: String channel (kept out of the final
  message text) alongside text and tool_call; MessageDelta::text/reasoning
  constructors.
- StreamAccumulator accumulates reasoning into a side buffer exposed via
  reasoning(); the merged ModelResponse text excludes it.
- Updated all MessageDelta literals (providers, openai, testkit, accumulator
  default stream) for the new field.
- Test: reasoning side-channel accumulation kept out of final text.
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