Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update prompts and invalidate cache #1082

Merged
merged 17 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from 8 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
2 changes: 2 additions & 0 deletions langfuse/api/__init__.py
Copy link
Contributor

Choose a reason for hiding this comment

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

Running this should get rid of all the file diffs due to formatting

poetry run ruff format langfuse/api --config ruff.toml

Copy link
Member Author

Choose a reason for hiding this comment

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

Thx! do you have an idea where this error here might come from?

ImportError while importing test module '/Users/maximiliandeichmann/development/github.com/langfuse/langfuse-python/tests/test_updating_prompt.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
../../../../.pyenv/versions/3.9.1/lib/python3.9/importlib/__init__.py:127: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
tests/test_updating_prompt.py:1: in <module>
    from langfuse.client import Langfuse
langfuse/__init__.py:3: in <module>
    from .client import Langfuse  # noqa
langfuse/client.py:43: in <module>
    from langfuse.api.resources.prompts.types import (
E   ImportError: cannot import name 'Prompt_Chat' from 'langfuse.api.resources.prompts.types' (/Users/maximiliandeichmann/development/github.com/langfuse/langfuse-python/langfuse/api/resources/prompts/types/__init__.py)

Somehow this fern run changed a lot i think.

Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@
models,
observations,
projects,
prompt_version,
prompts,
score,
score_configs,
Expand Down Expand Up @@ -302,6 +303,7 @@
"models",
"observations",
"projects",
"prompt_version",
"prompts",
"score",
"score_configs",
Expand Down
8 changes: 8 additions & 0 deletions langfuse/api/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
from .resources.models.client import AsyncModelsClient, ModelsClient
from .resources.observations.client import AsyncObservationsClient, ObservationsClient
from .resources.projects.client import AsyncProjectsClient, ProjectsClient
from .resources.prompt_version.client import (
AsyncPromptVersionClient,
PromptVersionClient,
)
from .resources.prompts.client import AsyncPromptsClient, PromptsClient
from .resources.score.client import AsyncScoreClient, ScoreClient
from .resources.score_configs.client import AsyncScoreConfigsClient, ScoreConfigsClient
Expand Down Expand Up @@ -108,6 +112,7 @@ def __init__(
self.models = ModelsClient(client_wrapper=self._client_wrapper)
self.observations = ObservationsClient(client_wrapper=self._client_wrapper)
self.projects = ProjectsClient(client_wrapper=self._client_wrapper)
self.prompt_version = PromptVersionClient(client_wrapper=self._client_wrapper)
self.prompts = PromptsClient(client_wrapper=self._client_wrapper)
self.score_configs = ScoreConfigsClient(client_wrapper=self._client_wrapper)
self.score = ScoreClient(client_wrapper=self._client_wrapper)
Expand Down Expand Up @@ -199,6 +204,9 @@ def __init__(
self.models = AsyncModelsClient(client_wrapper=self._client_wrapper)
self.observations = AsyncObservationsClient(client_wrapper=self._client_wrapper)
self.projects = AsyncProjectsClient(client_wrapper=self._client_wrapper)
self.prompt_version = AsyncPromptVersionClient(
client_wrapper=self._client_wrapper
)
self.prompts = AsyncPromptsClient(client_wrapper=self._client_wrapper)
self.score_configs = AsyncScoreConfigsClient(
client_wrapper=self._client_wrapper
Expand Down
94 changes: 94 additions & 0 deletions langfuse/api/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -2237,6 +2237,100 @@ client.projects.get()
</dl>


</dd>
</dl>
</details>

## PromptVersion
<details><summary><code>client.prompt_version.<a href="src/langfuse/resources/prompt_version/client.py">update</a>(...)</code></summary>
<dl>
<dd>

#### 📝 Description

<dl>
<dd>

<dl>
<dd>

Update labels for a specific prompt version
</dd>
</dl>
</dd>
</dl>

#### 🔌 Usage

<dl>
<dd>

<dl>
<dd>

```python
from langfuse.client import FernLangfuse

client = FernLangfuse(
x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME",
x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION",
x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY",
username="YOUR_USERNAME",
password="YOUR_PASSWORD",
base_url="https://yourhost.com/path/to/api",
)
client.prompt_version.update(
prompt_name="string",
version=1,
new_labels=["string"],
)

```
</dd>
</dl>
</dd>
</dl>

#### ⚙️ Parameters

<dl>
<dd>

<dl>
<dd>

**prompt_name:** `str` — The name of the prompt

</dd>
</dl>

<dl>
<dd>

**version:** `int` — Version of the prompt to update

</dd>
</dl>

<dl>
<dd>

**new_labels:** `typing.Sequence[str]` — New labels for the prompt version. Labels are unique across versions. The "latest" label is reserved and managed by Langfuse.

</dd>
</dl>

<dl>
<dd>

**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.

</dd>
</dl>
</dd>
</dl>


</dd>
</dl>
</details>
Expand Down
2 changes: 2 additions & 0 deletions langfuse/api/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
models,
observations,
projects,
prompt_version,
prompts,
score,
score_configs,
Expand Down Expand Up @@ -299,6 +300,7 @@
"models",
"observations",
"projects",
"prompt_version",
"prompts",
"score",
"score_configs",
Expand Down
2 changes: 2 additions & 0 deletions langfuse/api/resources/prompt_version/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# This file was auto-generated by Fern from our API Definition.

197 changes: 197 additions & 0 deletions langfuse/api/resources/prompt_version/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
# This file was auto-generated by Fern from our API Definition.

import typing
from json.decoder import JSONDecodeError

from ...core.api_error import ApiError
from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
from ...core.jsonable_encoder import jsonable_encoder
from ...core.pydantic_utilities import pydantic_v1
from ...core.request_options import RequestOptions
from ..commons.errors.access_denied_error import AccessDeniedError
from ..commons.errors.error import Error
from ..commons.errors.method_not_allowed_error import MethodNotAllowedError
from ..commons.errors.not_found_error import NotFoundError
from ..commons.errors.unauthorized_error import UnauthorizedError
from ..prompts.types.prompt import Prompt

# this is used as the default value for optional parameters
OMIT = typing.cast(typing.Any, ...)


class PromptVersionClient:
def __init__(self, *, client_wrapper: SyncClientWrapper):
self._client_wrapper = client_wrapper

def update(
self,
prompt_name: str,
version: int,
*,
new_labels: typing.Sequence[str],
request_options: typing.Optional[RequestOptions] = None,
) -> Prompt:
"""
Update labels for a specific prompt version

Parameters
----------
prompt_name : str
The name of the prompt

version : int
Version of the prompt to update

new_labels : typing.Sequence[str]
New labels for the prompt version. Labels are unique across versions. The "latest" label is reserved and managed by Langfuse.

request_options : typing.Optional[RequestOptions]
Request-specific configuration.

Returns
-------
Prompt

Examples
--------
from langfuse.client import FernLangfuse

client = FernLangfuse(
x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME",
x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION",
x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY",
username="YOUR_USERNAME",
password="YOUR_PASSWORD",
base_url="https://yourhost.com/path/to/api",
)
client.prompt_version.update(
prompt_name="string",
version=1,
new_labels=["string"],
)
"""
_response = self._client_wrapper.httpx_client.request(
f"api/public/v2/prompts/{jsonable_encoder(prompt_name)}/version/{jsonable_encoder(version)}",
maxdeichmann marked this conversation as resolved.
Show resolved Hide resolved
method="PATCH",
json={"newLabels": new_labels},
request_options=request_options,
omit=OMIT,
)
try:
if 200 <= _response.status_code < 300:
return pydantic_v1.parse_obj_as(Prompt, _response.json()) # type: ignore
if _response.status_code == 400:
raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore
if _response.status_code == 401:
raise UnauthorizedError(
pydantic_v1.parse_obj_as(typing.Any, _response.json())
) # type: ignore
if _response.status_code == 403:
raise AccessDeniedError(
pydantic_v1.parse_obj_as(typing.Any, _response.json())
) # type: ignore
if _response.status_code == 405:
raise MethodNotAllowedError(
pydantic_v1.parse_obj_as(typing.Any, _response.json())
) # type: ignore
if _response.status_code == 404:
raise NotFoundError(
pydantic_v1.parse_obj_as(typing.Any, _response.json())
) # type: ignore
_response_json = _response.json()
except JSONDecodeError:
raise ApiError(status_code=_response.status_code, body=_response.text)
raise ApiError(status_code=_response.status_code, body=_response_json)


class AsyncPromptVersionClient:
def __init__(self, *, client_wrapper: AsyncClientWrapper):
self._client_wrapper = client_wrapper

async def update(
self,
prompt_name: str,
version: int,
*,
new_labels: typing.Sequence[str],
request_options: typing.Optional[RequestOptions] = None,
) -> Prompt:
"""
Update labels for a specific prompt version

Parameters
----------
prompt_name : str
The name of the prompt

version : int
Version of the prompt to update

new_labels : typing.Sequence[str]
New labels for the prompt version. Labels are unique across versions. The "latest" label is reserved and managed by Langfuse.

request_options : typing.Optional[RequestOptions]
Request-specific configuration.

Returns
-------
Prompt

Examples
--------
import asyncio

from langfuse.client import AsyncFernLangfuse

client = AsyncFernLangfuse(
x_langfuse_sdk_name="YOUR_X_LANGFUSE_SDK_NAME",
x_langfuse_sdk_version="YOUR_X_LANGFUSE_SDK_VERSION",
x_langfuse_public_key="YOUR_X_LANGFUSE_PUBLIC_KEY",
username="YOUR_USERNAME",
password="YOUR_PASSWORD",
base_url="https://yourhost.com/path/to/api",
)


async def main() -> None:
await client.prompt_version.update(
prompt_name="string",
version=1,
new_labels=["string"],
)


asyncio.run(main())
"""
_response = await self._client_wrapper.httpx_client.request(
f"api/public/v2/prompts/{jsonable_encoder(prompt_name)}/version/{jsonable_encoder(version)}",
method="PATCH",
json={"newLabels": new_labels},
request_options=request_options,
omit=OMIT,
)
try:
if 200 <= _response.status_code < 300:
return pydantic_v1.parse_obj_as(Prompt, _response.json()) # type: ignore
if _response.status_code == 400:
raise Error(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore
if _response.status_code == 401:
raise UnauthorizedError(
pydantic_v1.parse_obj_as(typing.Any, _response.json())
) # type: ignore
if _response.status_code == 403:
raise AccessDeniedError(
pydantic_v1.parse_obj_as(typing.Any, _response.json())
) # type: ignore
if _response.status_code == 405:
raise MethodNotAllowedError(
pydantic_v1.parse_obj_as(typing.Any, _response.json())
) # type: ignore
if _response.status_code == 404:
raise NotFoundError(
pydantic_v1.parse_obj_as(typing.Any, _response.json())
) # type: ignore
_response_json = _response.json()
except JSONDecodeError:
raise ApiError(status_code=_response.status_code, body=_response.text)
raise ApiError(status_code=_response.status_code, body=_response_json)
26 changes: 26 additions & 0 deletions langfuse/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1357,6 +1357,32 @@ def create_prompt(
handle_fern_exception(e)
raise e

def update_prompt(
self,
*,
prompt_name: str,
hassiebp marked this conversation as resolved.
Show resolved Hide resolved
prompt_version: int,
new_labels: List[str] = [],
):
"""Update an existing prompt version in Langfuse. The Langfuse SDK prompt cache is invalidated for all prompts witht he specified name.

Args:
prompt_id (str): The name/id of the prompt to update.
prompt_version (int): The version number of the prompt to update.
new_labels (List[str], optional): New labels to assign to the prompt version. Labels are unique across versions. The "latest" label is reserved and managed by Langfuse. Defaults to [].

Returns:
Prompt: The updated prompt from the Langfuse API.

"""
updated_prompt = self.client.prompt_version.update(
prompt_name=prompt_name,
version=prompt_version,
new_labels=new_labels,
)
self.prompt_cache.invalidate(prompt_name)
return updated_prompt

def _url_encode(self, url: str) -> str:
return urllib.parse.quote(url)

Expand Down
Loading
Loading