mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
[Feat] Prompt Versioning - Allow specifying prompt version in code (#16929)
* add _get_prompt_data_from_dotprompt_content * fix pre call hook for prompt template * fix: get_latest_version_prompt_id * fix get_latest_version_prompt_id * test_get_latest_version_prompt_id
This commit is contained in:
parent
4b25398afe
commit
6ae22908b7
5 changed files with 235 additions and 2 deletions
|
|
@ -25,6 +25,23 @@ def set_global_prompt_directory(directory: str) -> None:
|
|||
|
||||
litellm.global_prompt_directory = directory # type: ignore
|
||||
|
||||
def _get_prompt_data_from_dotprompt_content(dotprompt_content: str) -> dict:
|
||||
"""
|
||||
Get the prompt data from the dotprompt content.
|
||||
|
||||
The UI stores prompts under `dotprompt_content` in the database. This function parses the content and returns the prompt data in the format expected by the prompt manager.
|
||||
"""
|
||||
from .prompt_manager import PromptManager
|
||||
|
||||
# Parse the dotprompt content to extract frontmatter and content
|
||||
temp_manager = PromptManager()
|
||||
metadata, content = temp_manager._parse_frontmatter(dotprompt_content)
|
||||
|
||||
# Convert to prompt_data format
|
||||
return {
|
||||
"content": content.strip(),
|
||||
"metadata": metadata
|
||||
}
|
||||
|
||||
def prompt_initializer(
|
||||
litellm_params: "PromptLiteLLMParams", prompt_spec: "PromptSpec"
|
||||
|
|
@ -41,6 +58,11 @@ def prompt_initializer(
|
|||
)
|
||||
|
||||
prompt_file = getattr(litellm_params, "prompt_file", None)
|
||||
|
||||
# Handle dotprompt_content from database
|
||||
dotprompt_content = getattr(litellm_params, "dotprompt_content", None)
|
||||
if dotprompt_content and not prompt_data and not prompt_file:
|
||||
prompt_data = _get_prompt_data_from_dotprompt_content(dotprompt_content)
|
||||
|
||||
try:
|
||||
dot_prompt_manager = DotpromptManager(
|
||||
|
|
|
|||
|
|
@ -19977,6 +19977,53 @@
|
|||
"supports_tool_choice": true,
|
||||
"supports_vision": true
|
||||
},
|
||||
"openrouter/google/gemini-3-pro-preview": {
|
||||
"cache_read_input_token_cost": 2e-07,
|
||||
"cache_read_input_token_cost_above_200k_tokens": 4e-07,
|
||||
"cache_creation_input_token_cost_above_200k_tokens": 2.5e-07,
|
||||
"input_cost_per_token": 2e-06,
|
||||
"input_cost_per_token_above_200k_tokens": 4e-06,
|
||||
"input_cost_per_token_batches": 1e-06,
|
||||
"litellm_provider": "openrouter",
|
||||
"max_audio_length_hours": 8.4,
|
||||
"max_audio_per_prompt": 1,
|
||||
"max_images_per_prompt": 3000,
|
||||
"max_input_tokens": 1048576,
|
||||
"max_output_tokens": 65535,
|
||||
"max_pdf_size_mb": 30,
|
||||
"max_tokens": 65535,
|
||||
"max_video_length": 1,
|
||||
"max_videos_per_prompt": 10,
|
||||
"mode": "chat",
|
||||
"output_cost_per_token": 1.2e-05,
|
||||
"output_cost_per_token_above_200k_tokens": 1.8e-05,
|
||||
"output_cost_per_token_batches": 6e-06,
|
||||
"supported_endpoints": [
|
||||
"/v1/chat/completions",
|
||||
"/v1/completions",
|
||||
"/v1/batch"
|
||||
],
|
||||
"supported_modalities": [
|
||||
"text",
|
||||
"image",
|
||||
"audio",
|
||||
"video"
|
||||
],
|
||||
"supported_output_modalities": [
|
||||
"text"
|
||||
],
|
||||
"supports_audio_input": true,
|
||||
"supports_function_calling": true,
|
||||
"supports_pdf_input": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_system_messages": true,
|
||||
"supports_tool_choice": true,
|
||||
"supports_video_input": true,
|
||||
"supports_vision": true,
|
||||
"supports_web_search": true
|
||||
},
|
||||
"openrouter/google/gemini-pro-1.5": {
|
||||
"input_cost_per_image": 0.00265,
|
||||
"input_cost_per_token": 2.5e-06,
|
||||
|
|
|
|||
|
|
@ -77,6 +77,72 @@ def get_version_number(prompt_id: str) -> int:
|
|||
return 1
|
||||
|
||||
|
||||
def construct_versioned_prompt_id(prompt_id: str, version: Optional[int] = None) -> str:
|
||||
"""
|
||||
Construct a versioned prompt ID from a base prompt_id and version number.
|
||||
|
||||
Args:
|
||||
prompt_id: Base prompt ID (e.g., "jack_success")
|
||||
version: Version number (if None, returns the base prompt_id unchanged)
|
||||
|
||||
Returns:
|
||||
Versioned prompt ID (e.g., "jack_success.v4")
|
||||
|
||||
Examples:
|
||||
>>> construct_versioned_prompt_id("jack_success", 4)
|
||||
"jack_success.v4"
|
||||
>>> construct_versioned_prompt_id("jack_success", None)
|
||||
"jack_success"
|
||||
>>> construct_versioned_prompt_id("jack_success.v2", 4)
|
||||
"jack_success.v4"
|
||||
"""
|
||||
if version is None:
|
||||
return prompt_id
|
||||
|
||||
# Strip any existing version suffix first
|
||||
base_id = get_base_prompt_id(prompt_id)
|
||||
return f"{base_id}.v{version}"
|
||||
|
||||
|
||||
def get_latest_version_prompt_id(prompt_id: str, all_prompt_ids: Dict[str, Any]) -> str:
|
||||
"""
|
||||
Find the latest version of a prompt from available prompt IDs.
|
||||
|
||||
Args:
|
||||
prompt_id: Base prompt ID or versioned prompt ID (e.g., "jack_success" or "jack_success.v2")
|
||||
all_prompt_ids: Dictionary of all available prompt IDs (keys are prompt IDs)
|
||||
|
||||
Returns:
|
||||
The prompt ID with the highest version number, or the original prompt_id if no versions exist
|
||||
|
||||
Examples:
|
||||
>>> all_ids = {"jack.v1": {}, "jack.v2": {}, "jack.v3": {}}
|
||||
>>> get_latest_version_prompt_id("jack", all_ids)
|
||||
"jack.v3"
|
||||
>>> get_latest_version_prompt_id("jack.v1", all_ids)
|
||||
"jack.v3"
|
||||
>>> all_ids = {"simple": {}}
|
||||
>>> get_latest_version_prompt_id("simple", all_ids)
|
||||
"simple"
|
||||
"""
|
||||
base_id = get_base_prompt_id(prompt_id=prompt_id)
|
||||
|
||||
# Find all versions of this prompt
|
||||
matching_versions = []
|
||||
for stored_prompt_id in all_prompt_ids.keys():
|
||||
if get_base_prompt_id(prompt_id=stored_prompt_id) == base_id:
|
||||
version_num = get_version_number(prompt_id=stored_prompt_id)
|
||||
matching_versions.append((version_num, stored_prompt_id))
|
||||
|
||||
# Use the highest version number
|
||||
if matching_versions:
|
||||
matching_versions.sort(reverse=True)
|
||||
return matching_versions[0][1]
|
||||
else:
|
||||
# No versioned prompts found, use the base ID as-is
|
||||
return prompt_id
|
||||
|
||||
|
||||
def get_latest_prompt_versions(prompts: List[PromptSpec]) -> List[PromptSpec]:
|
||||
"""
|
||||
Filter a list of prompts to return only the latest version of each unique prompt.
|
||||
|
|
|
|||
|
|
@ -894,6 +894,7 @@ class ProxyLogging:
|
|||
Optional["LiteLLMLoggingObj"], data.get("litellm_logging_obj", None)
|
||||
)
|
||||
prompt_id = data.get("prompt_id", None)
|
||||
prompt_version = data.get("prompt_version", None)
|
||||
|
||||
## PROMPT TEMPLATE CHECK ##
|
||||
if (
|
||||
|
|
@ -901,12 +902,28 @@ class ProxyLogging:
|
|||
and prompt_id is not None
|
||||
and (call_type == "completion" or call_type == "acompletion")
|
||||
):
|
||||
from litellm.proxy.prompts.prompt_endpoints import (
|
||||
construct_versioned_prompt_id,
|
||||
get_latest_version_prompt_id,
|
||||
)
|
||||
from litellm.proxy.prompts.prompt_registry import IN_MEMORY_PROMPT_REGISTRY
|
||||
|
||||
# If no version is specified, find the latest version
|
||||
if prompt_version is None:
|
||||
lookup_prompt_id = get_latest_version_prompt_id(
|
||||
prompt_id=prompt_id,
|
||||
all_prompt_ids=IN_MEMORY_PROMPT_REGISTRY.IN_MEMORY_PROMPTS,
|
||||
)
|
||||
else:
|
||||
# Construct versioned prompt_id if prompt_version is provided
|
||||
lookup_prompt_id = construct_versioned_prompt_id(
|
||||
prompt_id=prompt_id, version=prompt_version
|
||||
)
|
||||
|
||||
custom_logger = IN_MEMORY_PROMPT_REGISTRY.get_prompt_callback_by_id(
|
||||
prompt_id
|
||||
lookup_prompt_id
|
||||
)
|
||||
prompt_spec = IN_MEMORY_PROMPT_REGISTRY.get_prompt_by_id(prompt_id)
|
||||
prompt_spec = IN_MEMORY_PROMPT_REGISTRY.get_prompt_by_id(lookup_prompt_id)
|
||||
litellm_prompt_id: Optional[str] = None
|
||||
if prompt_spec is not None:
|
||||
litellm_prompt_id = prompt_spec.litellm_params.prompt_id
|
||||
|
|
|
|||
|
|
@ -104,6 +104,87 @@ class TestPromptVersioning:
|
|||
assert get_base_prompt_id(prompt_id="jack") == "jack"
|
||||
assert get_base_prompt_id(prompt_id="my_prompt.v10") == "my_prompt"
|
||||
|
||||
def test_get_latest_version_prompt_id(self):
|
||||
"""
|
||||
Test that get_latest_version_prompt_id returns the highest version
|
||||
"""
|
||||
from litellm.proxy.prompts.prompt_endpoints import get_latest_version_prompt_id
|
||||
|
||||
# Mock prompt IDs dictionary
|
||||
all_prompt_ids = {
|
||||
"jack.v1": {},
|
||||
"jack.v2": {},
|
||||
"jack.v3": {},
|
||||
"jane.v1": {},
|
||||
"simple_prompt": {},
|
||||
}
|
||||
|
||||
# Test with base prompt ID - should return latest version
|
||||
assert get_latest_version_prompt_id(
|
||||
prompt_id="jack",
|
||||
all_prompt_ids=all_prompt_ids
|
||||
) == "jack.v3"
|
||||
|
||||
# Test with versioned prompt ID - should still return latest version
|
||||
assert get_latest_version_prompt_id(
|
||||
prompt_id="jack.v1",
|
||||
all_prompt_ids=all_prompt_ids
|
||||
) == "jack.v3"
|
||||
|
||||
# Test with single version
|
||||
assert get_latest_version_prompt_id(
|
||||
prompt_id="jane",
|
||||
all_prompt_ids=all_prompt_ids
|
||||
) == "jane.v1"
|
||||
|
||||
# Test with non-versioned prompt
|
||||
assert get_latest_version_prompt_id(
|
||||
prompt_id="simple_prompt",
|
||||
all_prompt_ids=all_prompt_ids
|
||||
) == "simple_prompt"
|
||||
|
||||
# Test with non-existent prompt
|
||||
assert get_latest_version_prompt_id(
|
||||
prompt_id="nonexistent",
|
||||
all_prompt_ids=all_prompt_ids
|
||||
) == "nonexistent"
|
||||
|
||||
def test_construct_versioned_prompt_id(self):
|
||||
"""
|
||||
Test that construct_versioned_prompt_id correctly builds versioned IDs
|
||||
"""
|
||||
from litellm.proxy.prompts.prompt_endpoints import construct_versioned_prompt_id
|
||||
|
||||
# Test with base prompt ID and version
|
||||
assert construct_versioned_prompt_id(
|
||||
prompt_id="jack_success",
|
||||
version=4
|
||||
) == "jack_success.v4"
|
||||
|
||||
# Test with None version - should return base ID unchanged
|
||||
assert construct_versioned_prompt_id(
|
||||
prompt_id="jack_success",
|
||||
version=None
|
||||
) == "jack_success"
|
||||
|
||||
# Test with existing versioned ID - should replace version
|
||||
assert construct_versioned_prompt_id(
|
||||
prompt_id="jack_success.v2",
|
||||
version=4
|
||||
) == "jack_success.v4"
|
||||
|
||||
# Test with hyphenated prompt ID
|
||||
assert construct_versioned_prompt_id(
|
||||
prompt_id="my-prompt",
|
||||
version=1
|
||||
) == "my-prompt.v1"
|
||||
|
||||
# Test with double-digit version
|
||||
assert construct_versioned_prompt_id(
|
||||
prompt_id="test_prompt",
|
||||
version=10
|
||||
) == "test_prompt.v10"
|
||||
|
||||
|
||||
class TestPromptVersionsEndpoint:
|
||||
"""
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue