Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions langfuse/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
scim,
score_configs,
scores,
skills,
trace,
unstable,
utils,
Expand Down Expand Up @@ -384,6 +385,22 @@
ScoreV3_Text,
TextScoreV3,
)
from .skills import (
CreateSkillVersionRequest,
DeleteSkillVersionResponse,
SkillFile,
SkillFileContent,
SkillFileContentsResponse,
SkillListPagination,
SkillMeta,
SkillMetaListResponse,
SkillVersion,
SkillVersionFileCreateInput,
SkillVersionFileInput,
SkillVersionFileReference,
UpdateSkillLabelsRequest,
UpdateSkillRequest,
)
from .trace import DeleteTraceResponse
_dynamic_imports: typing.Dict[str, str] = {
"AccessDeniedError": ".commons",
Expand Down Expand Up @@ -462,6 +479,7 @@
"CreateScoreValue": ".commons",
"CreateScoresRequest": ".scores",
"CreateScoresResponse": ".scores",
"CreateSkillVersionRequest": ".skills",
"CreateTextPromptRequest": ".prompts",
"CreateTextPromptType": ".prompts",
"Creator": ".evaluators",
Expand All @@ -478,6 +496,7 @@
"DeleteDatasetItemResponse": ".dataset_items",
"DeleteLlmConnectionResponse": ".llm_connections",
"DeleteMembershipRequest": ".organizations",
"DeleteSkillVersionResponse": ".skills",
"DeleteTraceResponse": ".trace",
"DeletedEvaluationRule": ".evaluation_rules",
"DeletedEvaluator": ".evaluators",
Expand Down Expand Up @@ -680,6 +699,16 @@
"Score_Text": ".commons",
"ServiceProviderConfig": ".scim",
"ServiceUnavailableError": ".health",
"SkillFile": ".skills",
"SkillFileContent": ".skills",
"SkillFileContentsResponse": ".skills",
"SkillListPagination": ".skills",
"SkillMeta": ".skills",
"SkillMetaListResponse": ".skills",
"SkillVersion": ".skills",
"SkillVersionFileCreateInput": ".skills",
"SkillVersionFileInput": ".skills",
"SkillVersionFileReference": ".skills",
"StringEvaluationRuleFilter": ".evaluation_commons",
"StringObjectEvaluationRuleFilter": ".evaluation_commons",
"StringOptionsEvaluationRuleFilter": ".evaluation_commons",
Expand All @@ -697,6 +726,8 @@
"UpdateEvaluatorRequest": ".evaluators",
"UpdateLlmAsJudgeEvaluatorRequest": ".evaluators",
"UpdateScoreConfigRequest": ".score_configs",
"UpdateSkillLabelsRequest": ".skills",
"UpdateSkillRequest": ".skills",
"UpsertLlmConnectionRequest": ".llm_connections",
"Usage": ".commons",
"UserMeta": ".scim",
Expand Down Expand Up @@ -726,6 +757,7 @@
"scim": ".scim",
"score_configs": ".score_configs",
"scores": ".scores",
"skills": ".skills",
"trace": ".trace",
"unstable": ".unstable",
"utils": ".utils",
Expand Down Expand Up @@ -836,6 +868,7 @@ def __dir__():
"CreateScoreValue",
"CreateScoresRequest",
"CreateScoresResponse",
"CreateSkillVersionRequest",
"CreateTextPromptRequest",
"CreateTextPromptType",
"Creator",
Expand All @@ -852,6 +885,7 @@ def __dir__():
"DeleteDatasetItemResponse",
"DeleteLlmConnectionResponse",
"DeleteMembershipRequest",
"DeleteSkillVersionResponse",
"DeleteTraceResponse",
"DeletedEvaluationRule",
"DeletedEvaluator",
Expand Down Expand Up @@ -1054,6 +1088,16 @@ def __dir__():
"Score_Text",
"ServiceProviderConfig",
"ServiceUnavailableError",
"SkillFile",
"SkillFileContent",
"SkillFileContentsResponse",
"SkillListPagination",
"SkillMeta",
"SkillMetaListResponse",
"SkillVersion",
"SkillVersionFileCreateInput",
"SkillVersionFileInput",
"SkillVersionFileReference",
"StringEvaluationRuleFilter",
"StringObjectEvaluationRuleFilter",
"StringOptionsEvaluationRuleFilter",
Expand All @@ -1071,6 +1115,8 @@ def __dir__():
"UpdateEvaluatorRequest",
"UpdateLlmAsJudgeEvaluatorRequest",
"UpdateScoreConfigRequest",
"UpdateSkillLabelsRequest",
"UpdateSkillRequest",
"UpsertLlmConnectionRequest",
"Usage",
"UserMeta",
Expand Down Expand Up @@ -1100,6 +1146,7 @@ def __dir__():
"scim",
"score_configs",
"scores",
"skills",
"trace",
"unstable",
"utils",
Expand Down
4 changes: 2 additions & 2 deletions langfuse/api/blob_storage_integrations/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def upsert_blob_storage_integration(
**Cloud-only integration deprecation gate (effective 2026-06-22):** On Langfuse Cloud, legacy export sources are only accepted for blob storage integrations created before 2026-06-22, regardless of project age. Requests that would create a new integration with `LEGACY_TRACES_OBSERVATIONS` or `LEGACY_TRACES_AND_ENRICHED_OBSERVATIONS` are rejected with HTTP 400. Use `OBSERVATIONS_V2` instead. Self-hosted deployments are unaffected.

export_field_groups : typing.Optional[typing.Sequence[BlobStorageExportFieldGroup]]
Field groups to include in each exported observation row. Applies to all export sources; must include `core` if provided. When omitted on create, the column default (all groups) applies. When omitted on update, the existing value is preserved.
Field groups to include in each exported observation and score row. Applies to all export sources; must include the observation `core` group if provided. Core score columns are always exported; score groups add columns on top. When omitted on create, all observation groups are selected and no score groups. When omitted on update, the existing value is preserved.

`exportFieldGroups` requires `exportSource` to be provided in the same request.

Expand Down Expand Up @@ -438,7 +438,7 @@ async def upsert_blob_storage_integration(
**Cloud-only integration deprecation gate (effective 2026-06-22):** On Langfuse Cloud, legacy export sources are only accepted for blob storage integrations created before 2026-06-22, regardless of project age. Requests that would create a new integration with `LEGACY_TRACES_OBSERVATIONS` or `LEGACY_TRACES_AND_ENRICHED_OBSERVATIONS` are rejected with HTTP 400. Use `OBSERVATIONS_V2` instead. Self-hosted deployments are unaffected.

export_field_groups : typing.Optional[typing.Sequence[BlobStorageExportFieldGroup]]
Field groups to include in each exported observation row. Applies to all export sources; must include `core` if provided. When omitted on create, the column default (all groups) applies. When omitted on update, the existing value is preserved.
Field groups to include in each exported observation and score row. Applies to all export sources; must include the observation `core` group if provided. Core score columns are always exported; score groups add columns on top. When omitted on create, all observation groups are selected and no score groups. When omitted on update, the existing value is preserved.

`exportFieldGroups` requires `exportSource` to be provided in the same request.

Expand Down
4 changes: 2 additions & 2 deletions langfuse/api/blob_storage_integrations/raw_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ def upsert_blob_storage_integration(
**Cloud-only integration deprecation gate (effective 2026-06-22):** On Langfuse Cloud, legacy export sources are only accepted for blob storage integrations created before 2026-06-22, regardless of project age. Requests that would create a new integration with `LEGACY_TRACES_OBSERVATIONS` or `LEGACY_TRACES_AND_ENRICHED_OBSERVATIONS` are rejected with HTTP 400. Use `OBSERVATIONS_V2` instead. Self-hosted deployments are unaffected.

export_field_groups : typing.Optional[typing.Sequence[BlobStorageExportFieldGroup]]
Field groups to include in each exported observation row. Applies to all export sources; must include `core` if provided. When omitted on create, the column default (all groups) applies. When omitted on update, the existing value is preserved.
Field groups to include in each exported observation and score row. Applies to all export sources; must include the observation `core` group if provided. Core score columns are always exported; score groups add columns on top. When omitted on create, all observation groups are selected and no score groups. When omitted on update, the existing value is preserved.

`exportFieldGroups` requires `exportSource` to be provided in the same request.

Expand Down Expand Up @@ -713,7 +713,7 @@ async def upsert_blob_storage_integration(
**Cloud-only integration deprecation gate (effective 2026-06-22):** On Langfuse Cloud, legacy export sources are only accepted for blob storage integrations created before 2026-06-22, regardless of project age. Requests that would create a new integration with `LEGACY_TRACES_OBSERVATIONS` or `LEGACY_TRACES_AND_ENRICHED_OBSERVATIONS` are rejected with HTTP 400. Use `OBSERVATIONS_V2` instead. Self-hosted deployments are unaffected.

export_field_groups : typing.Optional[typing.Sequence[BlobStorageExportFieldGroup]]
Field groups to include in each exported observation row. Applies to all export sources; must include `core` if provided. When omitted on create, the column default (all groups) applies. When omitted on update, the existing value is preserved.
Field groups to include in each exported observation and score row. Applies to all export sources; must include the observation `core` group if provided. Core score columns are always exported; score groups add columns on top. When omitted on create, all observation groups are selected and no score groups. When omitted on update, the existing value is preserved.

`exportFieldGroups` requires `exportSource` to be provided in the same request.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@

class BlobStorageExportFieldGroup(enum.StrEnum):
"""
Field group selecting which observation columns are included in the export. Applies to all export sources; groups without a counterpart in the legacy data model (e.g. `trace_context`) are omitted from the legacy observations export.
Field group selecting which columns are included in the export. Applies to all export sources.
- Observation groups (`core` through `trace_context`) select observation columns; groups without a counterpart in the legacy data model (e.g. `trace_context`) are omitted from the legacy observations export.
- Score groups (`score_` prefix) add optional columns to the scores export. The core score columns (`id`, `timestamp`, `trace_id`, `name`, `value`, …) are always exported and are not a selectable group; score groups add columns on top of them. `score_metadata` adds the score `metadata` column. `score_provenance` adds the references to where a score came from: `author_user_id`, `config_id`, `queue_id`, `evaluator_id`, `evaluation_rule_id`, `execution_trace_id`. Score groups are never selected by default.
"""

CORE = "core"
Expand All @@ -23,6 +25,8 @@ class BlobStorageExportFieldGroup(enum.StrEnum):
METRICS = "metrics"
TOOLS = "tools"
TRACE_CONTEXT = "trace_context"
SCORE_METADATA = "score_metadata"
SCORE_PROVENANCE = "score_provenance"

def visit(
self,
Expand All @@ -37,6 +41,8 @@ def visit(
metrics: typing.Callable[[], T_Result],
tools: typing.Callable[[], T_Result],
trace_context: typing.Callable[[], T_Result],
score_metadata: typing.Callable[[], T_Result],
score_provenance: typing.Callable[[], T_Result],
Comment on lines +44 to +45

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Existing visitor calls fail

The two new required arguments break existing calls to BlobStorageExportFieldGroup.visit(), even for unchanged values such as CORE. A caller supplying all eleven callbacks required before this PR now gets TypeError before its callback runs. Make the new callbacks optional and check them only when visiting the new score groups.

Prompt To Fix With AI
This is a comment left during a code review.
Path: langfuse/api/blob_storage_integrations/types/blob_storage_export_field_group.py
Line: 44-45

Comment:
**Existing visitor calls fail**

The two new required arguments break existing calls to `BlobStorageExportFieldGroup.visit()`, even for unchanged values such as `CORE`. A caller supplying all eleven callbacks required before this PR now gets `TypeError` before its callback runs. Make the new callbacks optional and check them only when visiting the new score groups.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

) -> T_Result:
if self is BlobStorageExportFieldGroup.CORE:
return core()
Expand All @@ -60,3 +66,7 @@ def visit(
return tools()
if self is BlobStorageExportFieldGroup.TRACE_CONTEXT:
return trace_context()
if self is BlobStorageExportFieldGroup.SCORE_METADATA:
return score_metadata()
if self is BlobStorageExportFieldGroup.SCORE_PROVENANCE:
return score_provenance()
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class BlobStorageIntegrationResponse(UniversalBaseModel):
FieldMetadata(alias="exportFieldGroups"),
] = pydantic.Field(default=None)
"""
Field groups included in each exported observation row. An empty list is treated as all groups during export.
Field groups included in each exported observation and score row. An empty list is treated as all observation groups and no score groups during export. Core score columns are always exported.
"""

next_sync_at: typing_extensions.Annotated[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ class CreateBlobStorageIntegrationRequest(UniversalBaseModel):
FieldMetadata(alias="exportFieldGroups"),
] = pydantic.Field(default=None)
"""
Field groups to include in each exported observation row. Applies to all export sources; must include `core` if provided. When omitted on create, the column default (all groups) applies. When omitted on update, the existing value is preserved.
Field groups to include in each exported observation and score row. Applies to all export sources; must include the observation `core` group if provided. Core score columns are always exported; score groups add columns on top. When omitted on create, all observation groups are selected and no score groups. When omitted on update, the existing value is preserved.

`exportFieldGroups` requires `exportSource` to be provided in the same request.
"""
Expand Down
19 changes: 19 additions & 0 deletions langfuse/api/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
from .scim.client import AsyncScimClient, ScimClient
from .score_configs.client import AsyncScoreConfigsClient, ScoreConfigsClient
from .scores.client import AsyncScoresClient, ScoresClient
from .skills.client import AsyncSkillsClient, SkillsClient
from .trace.client import AsyncTraceClient, TraceClient
from .unstable.client import AsyncUnstableClient, UnstableClient

Expand Down Expand Up @@ -147,6 +148,7 @@ def __init__(
self._scim: typing.Optional[ScimClient] = None
self._score_configs: typing.Optional[ScoreConfigsClient] = None
self._scores: typing.Optional[ScoresClient] = None
self._skills: typing.Optional[SkillsClient] = None
self._trace: typing.Optional[TraceClient] = None
self._unstable: typing.Optional[UnstableClient] = None

Expand Down Expand Up @@ -352,6 +354,14 @@ def scores(self):
self._scores = ScoresClient(client_wrapper=self._client_wrapper)
return self._scores

@property
def skills(self):
if self._skills is None:
from .skills.client import SkillsClient # noqa: E402

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Imports break the repository rule

The new skills properties import their clients inside methods. The repository requires imports at the top of the module instead. Move these imports to module scope before merging. The async skills property and the topics properties in langfuse/api/unstable/client.py repeat this pattern.

Rule Used: Move imports to the top of the module instead of placing them within functions or methods. (source)

Learned From
langfuse/langfuse-python#1387

Prompt To Fix With AI
This is a comment left during a code review.
Path: langfuse/api/client.py
Line: 360

Comment:
**Imports break the repository rule**

The new `skills` properties import their clients inside methods. The repository requires imports at the top of the module instead. Move these imports to module scope before merging. The async `skills` property and the `topics` properties in `langfuse/api/unstable/client.py` repeat this pattern.

**Rule Used:** Move imports to the top of the module instead of placing them within functions or methods. ([source](https://app.greptile.com/personal-org-4986/-/custom-context?memory=c960fc07-9928-409f-a18b-a780cbdded12))

**Learned From**
[langfuse/langfuse-python#1387](https://github.com/langfuse/langfuse-python/pull/1387)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!


self._skills = SkillsClient(client_wrapper=self._client_wrapper)
return self._skills

@property
def trace(self):
if self._trace is None:
Expand Down Expand Up @@ -472,6 +482,7 @@ def __init__(
self._scim: typing.Optional[AsyncScimClient] = None
self._score_configs: typing.Optional[AsyncScoreConfigsClient] = None
self._scores: typing.Optional[AsyncScoresClient] = None
self._skills: typing.Optional[AsyncSkillsClient] = None
self._trace: typing.Optional[AsyncTraceClient] = None
self._unstable: typing.Optional[AsyncUnstableClient] = None

Expand Down Expand Up @@ -685,6 +696,14 @@ def scores(self):
self._scores = AsyncScoresClient(client_wrapper=self._client_wrapper)
return self._scores

@property
def skills(self):
if self._skills is None:
from .skills.client import AsyncSkillsClient # noqa: E402

self._skills = AsyncSkillsClient(client_wrapper=self._client_wrapper)
return self._skills

@property
def trace(self):
if self._trace is None:
Expand Down
6 changes: 5 additions & 1 deletion langfuse/api/commons/types/score_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ class ScoreConfig(UniversalBaseModel):
"""

id: str
name: str
name: str = pydantic.Field()
"""
Score config name. Maximum 80 characters.
"""

created_at: typing_extensions.Annotated[
dt.datetime, FieldMetadata(alias="createdAt")
]
Expand Down
Loading
Loading