Repository navigation
feat(client): support organization API keys with project_id - #2074
Draft
marksalpeter wants to merge 1 commit into
Draft
marksalpeter wants to merge 1 commit into
marksalpeter wants to merge 1 commit into
Conversation
Organization API keys can write to any project in their organization, so each request has to name its target project in the x-langfuse-project-id header. Add a project_id argument (env LANGFUSE_PROJECT_ID) to Langfuse() and identify a client by (public_key, project_id) everywhere: - LangfuseResourceManager instances are keyed by the pair, so a second client on the same org key for another project no longer silently reuses the first client's setup. - Tracer scope attributes carry project_id and LangfuseSpanProcessor filters on both, so two clients sharing an org key never export each other's spans. - get_client() and @observe accept project_id / langfuse_project_id; an org key shared by several clients without a project_id resolves to a disabled client. The header is sent on OTLP exports and on the generated API clients (so a caller-provided httpx_client sends it too). A conflicting project header in additional_headers or httpx_client.headers raises ValueError at init. A configured project_id is used for trace and experiment URLs without calling projects.get(). A custom span_exporter with project_id warns that the exporter must set the header, and a 403 span export logs a one-time hint to set LANGFUSE_PROJECT_ID. Project API keys leave project_id unset, so their behavior is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016PExPf2jnm8CPvdxKYmfqt
Author
|
Resolved: the CLA is now signed ( Generated by Claude Code |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Adds SDK support for organization API keys (LFE-18172). An org key can write to any project in its organization, so every request has to name its target project in the
x-langfuse-project-idheader.New option:
Langfuse(project_id=...), or theLANGFUSE_PROJECT_IDenv var. The argument takes precedence.Fixes a cross-project leak. Clients used to be identified by
public_keyonly. With one org key used for two projects,Langfuse(public_key=ORG, project_id="B")silently reused project A's client, and each client's span processor exported the other's spans. A client is now identified by(public_key, project_id)in:LangfuseResourceManager._instancesregistry key;project_idnext topublic_key) and theLangfuseSpanProcessorfilter;get_client(public_key=..., project_id=...). If several clients share a public key and noproject_idis given, it returns a disabled client and logs a warning, like today's "multiple clients, no key" case;@observe(langfuse_project_id=...)and the execution-context var that nested decorated functions read.Header: sent on OTLP span exports and on the generated
LangfuseAPI/AsyncLangfuseAPIclients. It is set on the generated clients rather than the httpx client, so a user-suppliedhttpx_clientsends it too.Conflict check: if
additional_headersorhttpx_client.headerssetsx-langfuse-project-id/langfuse-project-id(any case) to a different value, init raisesValueError. The same value is accepted. A header set withoutproject_idis left alone, so the existingadditional_headersworkaround keeps working.URLs: with
project_idconfigured,_get_project_id()returns it without callingprojects.get(). This covers trace URLs and experiment URLs.Warnings and hints:
project_idtogether with a customspan_exporterlogs a warning that the exporter must set the header itself.LANGFUSE_PROJECT_ID/project_id.Project API keys leave
project_idunset, so their behavior is unchanged.Notes for reviewers
logging.Filteron the exporter's logger. It matches the exporter'sFailed to export ... code: 403record, fires once per process, and never suppresses the record. If a future OTel release rewords that message, the hint stops appearing and nothing else changes.auth_check()still callsprojects.get(). With the header sent, the server resolves the header's project, so this should work for org keys whose role grantsproject:read.langfuse_public_keyand LangChainCallbackHandler(public_key=...)don't take a project ID yet. With an org key shared by several clients, they get a disabled client. Inside@observethey already pick the right client from context. This can be a follow-up.TracerProvider. The existingmulti_project_setupfixture mocks the processor constructor, so it doesn't exercise the scope filter. I checked that the test fails when theproject_idcheck is removed from the filter.Type of change
Verification
List the main commands you ran:
Unit tests: 1031 passed. 18 tests in
tests/unit/test_prompt.pyerror locally becauseLANGFUSE_PUBLIC_KEYisn't set in the environment; they error the same way onmain. I didn't run e2e locally. The only e2e change is thetests/e2e/test_decorators.pycleanup helper, updated for the new registry key shape.Checklist
code_review.md..env.templateif needed.Generated by Claude Code