feat: add logfire.url_from_eval(report) method#1694
Conversation
Deploying logfire-docs with
|
| Latest commit: |
c3b864c
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://b7e8b84d.logfire-docs.pages.dev |
| Branch Preview URL: | https://feat-url-from-eval.logfire-docs.pages.dev |
b86eb5c to
8cf0b55
Compare
logfire.url_from_eval(report) methodlogfire.url_from_eval(report) method
edb283a to
dd8d80b
Compare
…rd links for eval reports Users running pydantic-evals evaluations can now easily get a Logfire dashboard link to view their evaluation report by calling `logfire.url_from_eval(report)`. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
dd8d80b to
d654bed
Compare
- Strip trailing slash from project_url before building URL - Load project_url from credentials file regardless of send_to_logfire - Add test for trailing slash handling Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
# Conflicts: # uv.lock
…_eval When the token is provided via env var without a credentials file, project_url is only populated in a background thread. url_from_eval now joins that thread first to ensure the URL is available. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Uses requests_mock to exercise the full path: configure with token (no creds file) -> background thread validates token -> url_from_eval waits for thread -> returns correct URL. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
I feel like we need a place to put functions that are specific to our web platform, ideally grouped a bit more by function. There are going to be variables soon, then datasets, and more to come. I feel like we should be a bit thoughtful about where the APIs go rather than just haphazardly adding methods to the logfire instance. But 🤷 if it unblocks stuff it's probably worth biasing toward action |
Use a generation counter so that check_tokens threads from a previous configure() call do not write a stale project_url. Also fix the test_url_from_eval_waits_for_token_validation test to use tmp_path so it does not read from a local credentials file. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Covers the generation-check branches in check_tokens to satisfy 100% coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use two tokens so the orphaned thread's second loop iteration hits the generation-mismatch early return. Also assert that Event.wait and Thread.join actually succeed within the timeout. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Save _check_tokens_thread to a local variable before checking and joining, so a concurrent configure() cannot set it to None between the two reads. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
logfire/_internal/config.py
Outdated
|
|
||
| # Try loading credentials from a file. | ||
| # We do this before checking send_to_logfire so that project_url | ||
| # is available for url_from_eval even when not sending data. |
There was a problem hiding this comment.
but should url_from_eval return something when not sending data?
There was a problem hiding this comment.
No - addressed in bca78cf. Credential loading moved back inside the send_to_logfire block, so url_from_eval returns None when not sending data.
logfire/_internal/config.py
Outdated
| with suppress_instrumentation(): | ||
| for token in token_list: | ||
| if self._config_generation != generation: | ||
| return |
There was a problem hiding this comment.
No longer applicable - the generation check was removed entirely in bca78cf.
logfire/_internal/config.py
Outdated
| raise | ||
| credentials = None | ||
| if credentials is not None: | ||
| self.project_url = self.project_url or credentials.project_url |
There was a problem hiding this comment.
| self.project_url = self.project_url or credentials.project_url | |
| self.project_url = credentials.project_url |
when would this differ?
If project_url is not available (i.e. not sending data to Logfire), url_from_eval returns None since the URL wouldn't work anyway. This removes the early credential loading, background thread waiting, and generation tracking that were added to support url_from_eval when not sending data. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the token comes from an env var without a creds file, project_url was never set because credentials was None at that point. Now check_tokens also sets project_url from the validated credentials returned by the /v1/info endpoint. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use ellipsis body style in logfire-api shim for url_from_eval - Use ImportError instead of Exception in test import guard
The import chain can fail with AttributeError (not ImportError) when pydantic is too old, since pydantic_evals transitively imports pydantic_ai which uses pydantic.Tag.
|
Re: "P3: After printing a validated token summary, add the token to printed_tokens" - not an issue. Each token appears at most once in |
|
I think we are good now @alexmojaki |
logfire/_internal/config.py
Outdated
| self.advanced = advanced | ||
|
|
||
| self.additional_span_processors = additional_span_processors | ||
| self.project_url: str | None = None |
There was a problem hiding this comment.
Add _ prefix project_url.
|
omg, I need to this manually |
2f51fbc to
d463c11
Compare
|
@alexmojaki applied manually the leading underscore. |
| def url_from_eval(self, report: EvaluationReport[Any, Any, Any]) -> str | None: | ||
| """Generate a Logfire URL to view an evaluation report. | ||
|
|
||
| Args: | ||
| report: An evaluation report from `pydantic_evals`. | ||
|
|
||
| Returns: | ||
| The URL string, or `None` if the project URL or trace/span IDs are not available. | ||
| """ | ||
| project_url = self._config._project_url # type: ignore[reportPrivateUsage] | ||
| trace_id = report.trace_id | ||
| span_id = report.span_id | ||
| if not project_url or not trace_id or not span_id: | ||
| return None | ||
| return f'{project_url.rstrip("/")}/evals/compare?experiment={trace_id}-{span_id}' |
There was a problem hiding this comment.
🚩 _project_url availability depends on async token validation completing
When Logfire is configured with a token from an environment variable (no credentials file), _project_url will remain None until the background thread at logfire/_internal/config.py:1121 completes the HTTP request to /v1/info and sets it at line 1114. If url_from_eval is called before this thread completes, it will return None even though the project URL will eventually be available. This is documented behavior ('or None if the project URL ... are not available'), but users may find it surprising if they call url_from_eval immediately after configure(). A potential improvement would be to provide a way to wait for the background thread to complete, or to document the timing dependency more prominently.
Was this helpful? React with 👍 or 👎 to provide feedback.
Summary
logfire.url_from_eval(report)method that generates a Logfire dashboard link to view apydantic_evals.EvaluationReportproject_urlonLogfireConfigfrom credentials (file or token validation){project_url}/evals/compare?experiment={trace_id}-{span_id}, returningNoneif any piece is missingpydantic-evalsas a dev dependency for type checkingTest plan
make typecheck— 0 errorsmake lint— all checks passeduv run pytest tests/test_url_from_eval.py— 5 tests covering happy path and allNonecasesuv run pytest tests/test_logfire_api.py— logfire-api shim tests pass🤖 Generated with Claude Code