5a558eb09e
TypeScript SDK Compatibility V1.x E2E Tests / Select Node version matrix (push) Has been cancelled
TypeScript SDK Compatibility V1.x E2E Tests / TypeScript SDK Compatibility V1.x E2E Tests Node ${{matrix.node_version}} (push) Has been cancelled
TypeScript SDK E2E Tests / TypeScript SDK E2E Tests Node ${{matrix.node_version}} (push) Has been cancelled
Opik Optimizer - E2E Tests / build-opik (push) Has been cancelled
TypeScript SDK Compatibility V1.x E2E Tests / build-opik (push) Has been cancelled
Python SDK E2E Tests / Select Python version matrix (push) Has been cancelled
Python SDK E2E Tests / Python SDK E2E Tests ${{matrix.python_version}} (push) Has been cancelled
Python SDK E2E Tests / build-opik (push) Has been cancelled
Python SDK Compatibility V1.x E2E Tests / Select Python version matrix (push) Has been cancelled
Python SDK Compatibility V1.x E2E Tests / Python SDK Compatibility V1.x E2E Tests ${{matrix.python_version}} (push) Has been cancelled
Python SDK Compatibility V1.x E2E Tests / build-opik (push) Has been cancelled
TypeScript SDK E2E Tests / Select Node version matrix (push) Has been cancelled
TypeScript SDK E2E Tests / build-opik (push) Has been cancelled
Opik Optimizer - E2E Tests / Opik Optimizer E2E Tests Python ${{matrix.python_version}} (push) Has been cancelled
Opik Optimizer - E2E Tests / Opik Optimizer Integration Smoke Tests (push) Has been cancelled
🐙 Code Quality / detect (push) Has been cancelled
🐙 Code Quality / lint (${{ matrix.leg.name }}) (push) Has been cancelled
🐙 Code Quality / summary (push) Has been cancelled
TypeScript SDK Library Integration Tests / Check Secrets (push) Has been cancelled
TypeScript SDK Library Integration Tests / opik-vercel (Vercel AI SDK / eve) (push) Has been cancelled
SDK Library Integration Tests Runner / Check Secrets (push) Has been cancelled
SDK Library Integration Tests Runner / Missed OpenAI API Key Warning (push) Has been cancelled
SDK Library Integration Tests Runner / Build (push) Has been cancelled
SDK Library Integration Tests Runner / openai_tests (push) Has been cancelled
SDK Library Integration Tests Runner / langchain_tests (push) Has been cancelled
SDK Library Integration Tests Runner / langchain_legacy_tests (push) Has been cancelled
SDK Library Integration Tests Runner / llama_index_tests (push) Has been cancelled
SDK Library Integration Tests Runner / anthropic_tests (push) Has been cancelled
SDK Library Integration Tests Runner / mistral_tests (push) Has been cancelled
SDK Library Integration Tests Runner / groq_tests (push) Has been cancelled
SDK Library Integration Tests Runner / aisuite_tests (push) Has been cancelled
SDK Library Integration Tests Runner / haystack_tests (push) Has been cancelled
SDK Library Integration Tests Runner / dspy_tests (push) Has been cancelled
SDK Library Integration Tests Runner / crewai_v0_tests (push) Has been cancelled
SDK Library Integration Tests Runner / crewai_v1_tests (push) Has been cancelled
SDK Library Integration Tests Runner / genai_tests (push) Has been cancelled
SDK Library Integration Tests Runner / adk_tests (push) Has been cancelled
SDK Library Integration Tests Runner / adk_legacy_1_3_0_tests (push) Has been cancelled
SDK Library Integration Tests Runner / evaluation_metrics_tests (push) Has been cancelled
SDK Library Integration Tests Runner / bedrock_tests (push) Has been cancelled
SDK Library Integration Tests Runner / litellm_tests (push) Has been cancelled
SDK Library Integration Tests Runner / harbor_tests (push) Has been cancelled
SDK Library Integration Tests Runner / Slack Notification (push) Has been cancelled
Lint Opik Helm Chart / render-equality (push) Has been cancelled
Opik Optimizer - Unit Tests / Opik Optimizer Unit Tests Python ${{matrix.python_version}} (push) Has been cancelled
Python BE E2E Tests / Python BE E2E (push) Has been cancelled
Python Backend Tests / run-python-backend-tests (push) Has been cancelled
Python SDK Unit Tests / Python SDK Unit Tests ${{matrix.python_version}} (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
SDK E2E Libraries Integration Tests / Check Secrets (push) Has been cancelled
SDK E2E Libraries Integration Tests / Missed OpenAI API Key Warning (push) Has been cancelled
SDK E2E Libraries Integration Tests / build-opik (push) Has been cancelled
SDK E2E Libraries Integration Tests / E2E Lib Integration Python ${{matrix.python_version}} (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-gemini) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-langchain) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-openai) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-otel) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-vercel) (push) Has been cancelled
TypeScript SDK Build & Publish / build-and-publish (push) Has been cancelled
TypeScript SDK Unit Tests / Test on Node ${{ matrix.node-version }} (push) Has been cancelled
Backend Tests / discover-tests (push) Has been cancelled
Backend Tests / ${{ matrix.name }} (push) Has been cancelled
Build and Publish SDK / build-and-publish (push) Has been cancelled
Build Opik Docker Images / set-version (push) Has been cancelled
Build Opik Docker Images / build-backend (push) Has been cancelled
Build Opik Docker Images / build-sandbox-executor-python (push) Has been cancelled
Build Opik Docker Images / build-python-backend (push) Has been cancelled
Build Opik Docker Images / build-frontend (push) Has been cancelled
Build Opik Docker Images / create-git-tag (push) Has been cancelled
ClickHouse Migration Cluster Check / validate-clickhouse-migrations (push) Has been cancelled
Docs - Publish / run (push) Has been cancelled
E2E Tests - Post Merge (v2) / 🧪 E2E v2 Tests (${{ github.event.inputs.tier || 't1' }}) (push) Has been cancelled
E2E Tests - Post Merge (v2) / 📢 Slack Notification (push) Has been cancelled
Frontend Unit Tests / Test on Node 20 (push) Has been cancelled
Guardrails E2E Tests / Select Python version matrix (push) Has been cancelled
Guardrails E2E Tests / Guardrails E2E Tests ${{matrix.python_version}} (push) Has been cancelled
Guardrails E2E Tests / 📢 Slack Notification (push) Has been cancelled
Guardrails Backend Unit Tests / Guardrails Backend Unit Tests (push) Has been cancelled
Guardrails Backend Unit Tests / 📢 Slack Notification (push) Has been cancelled
Lint Opik Helm Chart / lint-helm-chart (Helm v3.21.0) (push) Has been cancelled
Lint Opik Helm Chart / lint-helm-chart (Helm v4.2.0) (push) Has been cancelled
Lint Opik Helm Chart / unittest-helm-chart (push) Has been cancelled
667 lines
22 KiB
Python
667 lines
22 KiB
Python
import uuid
|
|
import opik
|
|
from opik.api_objects.prompt import PromptType, ChatPrompt
|
|
from . import verifiers
|
|
from ..testlib import generate_project_name
|
|
import opik.exceptions
|
|
import pytest
|
|
|
|
PROJECT_NAME = generate_project_name("e2e", __name__)
|
|
|
|
|
|
def test_chat_prompt__create__happyflow(opik_client: opik.Opik):
|
|
"""Test creating a chat prompt with multiple messages."""
|
|
unique_identifier = str(uuid.uuid4())[-6:]
|
|
|
|
prompt_name = f"chat-prompt-{unique_identifier}"
|
|
messages = [
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{"role": "user", "content": "Hello, {{name}}!"},
|
|
]
|
|
|
|
chat_prompt = opik_client.create_chat_prompt(
|
|
name=prompt_name,
|
|
messages=messages,
|
|
metadata={"version": "1.0", "type": "customer_support"},
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
# Verify the prompt was created correctly
|
|
verifiers.verify_chat_prompt_version(
|
|
chat_prompt,
|
|
name=prompt_name,
|
|
messages=messages,
|
|
metadata={"version": "1.0", "type": "customer_support"},
|
|
type=PromptType.MUSTACHE,
|
|
)
|
|
|
|
|
|
def test_chat_prompt__format__happyflow(opik_client: opik.Opik):
|
|
"""Test formatting a chat prompt with variables."""
|
|
unique_identifier = str(uuid.uuid4())[-6:]
|
|
|
|
prompt_name = f"chat-prompt-format-{unique_identifier}"
|
|
messages = [
|
|
{"role": "system", "content": "You are a {{role}}."},
|
|
{"role": "user", "content": "Hello, {{name}}! Tell me about {{topic}}."},
|
|
]
|
|
|
|
chat_prompt = opik_client.create_chat_prompt(
|
|
name=prompt_name,
|
|
messages=messages,
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
# Verify the prompt was created correctly
|
|
verifiers.verify_chat_prompt_version(
|
|
chat_prompt,
|
|
name=prompt_name,
|
|
messages=messages,
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
# Format with variables
|
|
formatted = chat_prompt.format(
|
|
variables={"role": "coding assistant", "name": "Alice", "topic": "Python"}
|
|
)
|
|
|
|
assert len(formatted) == 2
|
|
assert formatted[0]["role"] == "system"
|
|
assert formatted[0]["content"] == "You are a coding assistant."
|
|
assert formatted[1]["role"] == "user"
|
|
assert formatted[1]["content"] == "Hello, Alice! Tell me about Python."
|
|
|
|
|
|
def test_chat_prompt__create_new_version__happyflow(opik_client: opik.Opik):
|
|
"""Test creating a new version of a chat prompt."""
|
|
unique_identifier = str(uuid.uuid4())[-6:]
|
|
|
|
prompt_name = f"chat-prompt-versioning-{unique_identifier}"
|
|
messages_v1 = [
|
|
{"role": "system", "content": "You are helpful."},
|
|
{"role": "user", "content": "Hi!"},
|
|
]
|
|
|
|
# Create initial version
|
|
chat_prompt_v1 = opik_client.create_chat_prompt(
|
|
name=prompt_name, messages=messages_v1, project_name=PROJECT_NAME
|
|
)
|
|
|
|
# Create new version with different messages
|
|
messages_v2 = [
|
|
{"role": "system", "content": "You are very helpful."},
|
|
{"role": "user", "content": "Hello there!"},
|
|
{"role": "assistant", "content": "How can I assist you?"},
|
|
]
|
|
|
|
chat_prompt_v2 = opik_client.create_chat_prompt(
|
|
name=prompt_name, messages=messages_v2, project_name=PROJECT_NAME
|
|
)
|
|
|
|
# Verify both versions
|
|
verifiers.verify_chat_prompt_version(
|
|
chat_prompt_v1,
|
|
name=prompt_name,
|
|
messages=messages_v1,
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
verifiers.verify_chat_prompt_version(
|
|
chat_prompt_v2,
|
|
name=prompt_name,
|
|
messages=messages_v2,
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
# Verify they share the same prompt ID but have different version IDs
|
|
assert (
|
|
chat_prompt_v2.__internal_api__prompt_id__
|
|
== chat_prompt_v1.__internal_api__prompt_id__
|
|
)
|
|
assert (
|
|
chat_prompt_v2.__internal_api__version_id__
|
|
!= chat_prompt_v1.__internal_api__version_id__
|
|
)
|
|
assert chat_prompt_v2.commit != chat_prompt_v1.commit
|
|
|
|
|
|
def test_chat_prompt__do_not_create_new_version_with_same_messages(
|
|
opik_client: opik.Opik,
|
|
):
|
|
"""Test that creating a chat prompt with identical messages doesn't create a new version."""
|
|
unique_identifier = str(uuid.uuid4())[-6:]
|
|
|
|
prompt_name = f"chat-prompt-no-dup-{unique_identifier}"
|
|
messages = [
|
|
{"role": "system", "content": "You are helpful."},
|
|
{"role": "user", "content": "Hello!"},
|
|
]
|
|
|
|
# Create initial version
|
|
chat_prompt_v1 = opik_client.create_chat_prompt(
|
|
name=prompt_name, messages=messages, project_name=PROJECT_NAME
|
|
)
|
|
|
|
# Try to create with same messages
|
|
chat_prompt_v2 = opik_client.create_chat_prompt(
|
|
name=prompt_name, messages=messages, project_name=PROJECT_NAME
|
|
)
|
|
|
|
# Verify both prompts
|
|
verifiers.verify_chat_prompt_version(
|
|
chat_prompt_v1,
|
|
name=prompt_name,
|
|
messages=messages,
|
|
commit=chat_prompt_v1.commit,
|
|
prompt_id=chat_prompt_v1.__internal_api__prompt_id__,
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
verifiers.verify_chat_prompt_version(
|
|
chat_prompt_v2,
|
|
name=prompt_name,
|
|
messages=messages,
|
|
commit=chat_prompt_v1.commit,
|
|
prompt_id=chat_prompt_v1.__internal_api__prompt_id__,
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
|
|
def test_chat_prompt__direct_class_instantiation__happyflow(opik_client: opik.Opik):
|
|
"""Test creating a ChatPrompt directly using the class."""
|
|
unique_identifier = str(uuid.uuid4())[-6:]
|
|
|
|
prompt_name = f"chat-prompt-direct-{unique_identifier}"
|
|
messages = [
|
|
{"role": "system", "content": "You are an expert."},
|
|
{"role": "user", "content": "Explain {{concept}}."},
|
|
]
|
|
|
|
# Create directly via ChatPrompt class
|
|
chat_prompt = ChatPrompt(
|
|
name=prompt_name,
|
|
messages=messages,
|
|
metadata={"category": "education"},
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
# Verify it was synced to backend
|
|
verifiers.verify_chat_prompt_version(
|
|
chat_prompt,
|
|
name=prompt_name,
|
|
messages=messages,
|
|
metadata={"category": "education"},
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
|
|
def test_chat_prompt__multimodal_content__happyflow(opik_client: opik.Opik):
|
|
"""Test chat prompt with multimodal content (text + images)."""
|
|
unique_identifier = str(uuid.uuid4())[-6:]
|
|
|
|
prompt_name = f"chat-prompt-multimodal-{unique_identifier}"
|
|
messages = [
|
|
{"role": "system", "content": "You analyze images."},
|
|
{
|
|
"role": "user",
|
|
"content": [
|
|
{"type": "text", "text": "What's in this image of {{subject}}?"},
|
|
{
|
|
"type": "image_url",
|
|
"image_url": {"url": "https://example.com/image.jpg"},
|
|
},
|
|
],
|
|
},
|
|
]
|
|
|
|
chat_prompt = opik_client.create_chat_prompt(
|
|
name=prompt_name,
|
|
messages=messages,
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
# Verify multimodal content is preserved
|
|
verifiers.verify_chat_prompt_version(
|
|
chat_prompt,
|
|
name=prompt_name,
|
|
messages=messages,
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
assert len(chat_prompt.template[1]["content"]) == 2
|
|
assert chat_prompt.template[1]["content"][0]["type"] == "text"
|
|
assert chat_prompt.template[1]["content"][1]["type"] == "image_url"
|
|
|
|
|
|
def test_chat_prompt__different_types__mustache_and_jinja2(opik_client: opik.Opik):
|
|
"""Test chat prompts with different template types."""
|
|
unique_identifier = str(uuid.uuid4())[-6:]
|
|
|
|
# Test with Mustache
|
|
prompt_name_mustache = f"chat-prompt-mustache-{unique_identifier}"
|
|
messages_mustache = [
|
|
{"role": "user", "content": "Hello {{name}}!"},
|
|
]
|
|
|
|
chat_prompt_mustache = opik_client.create_chat_prompt(
|
|
name=prompt_name_mustache,
|
|
messages=messages_mustache,
|
|
type=PromptType.MUSTACHE,
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
verifiers.verify_chat_prompt_version(
|
|
chat_prompt_mustache,
|
|
name=prompt_name_mustache,
|
|
messages=messages_mustache,
|
|
type=PromptType.MUSTACHE,
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
formatted_mustache = chat_prompt_mustache.format(variables={"name": "Bob"})
|
|
assert formatted_mustache[0]["content"] == "Hello Bob!"
|
|
|
|
# Test with Jinja2
|
|
prompt_name_jinja = f"chat-prompt-jinja-{unique_identifier}"
|
|
messages_jinja = [
|
|
{"role": "user", "content": "Hello {{ name }}!"},
|
|
]
|
|
|
|
chat_prompt_jinja = opik_client.create_chat_prompt(
|
|
name=prompt_name_jinja,
|
|
messages=messages_jinja,
|
|
type=PromptType.JINJA2,
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
verifiers.verify_chat_prompt_version(
|
|
chat_prompt_jinja,
|
|
name=prompt_name_jinja,
|
|
messages=messages_jinja,
|
|
type=PromptType.JINJA2,
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
formatted_jinja = chat_prompt_jinja.format(variables={"name": "Carol"})
|
|
assert formatted_jinja[0]["content"] == "Hello Carol!"
|
|
|
|
|
|
def test_chat_prompt__empty_messages__should_work(opik_client: opik.Opik):
|
|
"""Test chat prompt with empty messages list."""
|
|
unique_identifier = str(uuid.uuid4())[-6:]
|
|
|
|
prompt_name = f"chat-prompt-empty-{unique_identifier}"
|
|
messages = []
|
|
|
|
chat_prompt = opik_client.create_chat_prompt(
|
|
name=prompt_name,
|
|
messages=messages,
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
verifiers.verify_chat_prompt_version(
|
|
chat_prompt,
|
|
name=prompt_name,
|
|
messages=[],
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
|
|
def test_chat_prompt__multiple_roles__happyflow(opik_client: opik.Opik):
|
|
"""Test chat prompt with system, user, and assistant messages."""
|
|
unique_identifier = str(uuid.uuid4())[-6:]
|
|
|
|
prompt_name = f"chat-prompt-multirole-{unique_identifier}"
|
|
messages = [
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{"role": "user", "content": "What is the capital of {{country}}?"},
|
|
{"role": "assistant", "content": "I'd be happy to help with that!"},
|
|
{"role": "user", "content": "Please tell me."},
|
|
]
|
|
|
|
chat_prompt = opik_client.create_chat_prompt(
|
|
name=prompt_name,
|
|
messages=messages,
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
verifiers.verify_chat_prompt_version(
|
|
chat_prompt,
|
|
name=prompt_name,
|
|
messages=messages,
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
formatted = chat_prompt.format(variables={"country": "France"})
|
|
assert len(formatted) == 4
|
|
assert formatted[0]["role"] == "system"
|
|
assert formatted[1]["role"] == "user"
|
|
assert formatted[1]["content"] == "What is the capital of France?"
|
|
assert formatted[2]["role"] == "assistant"
|
|
assert formatted[3]["role"] == "user"
|
|
|
|
|
|
def test_get_chat_prompt__chat_prompt__returns_chat_prompt(opik_client: opik.Opik):
|
|
"""Test that get_chat_prompt() returns a ChatPrompt object for chat prompts."""
|
|
unique_id = str(uuid.uuid4())[-6:]
|
|
prompt_name = f"chat-prompt-get-{unique_id}"
|
|
|
|
# Create a chat prompt
|
|
created_prompt = opik_client.create_chat_prompt(
|
|
name=prompt_name,
|
|
messages=[
|
|
{"role": "system", "content": "You are helpful"},
|
|
{"role": "user", "content": "Hello {{name}}"},
|
|
],
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
# Retrieve it with get_chat_prompt()
|
|
retrieved_prompt = opik_client.get_chat_prompt(
|
|
name=prompt_name,
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
assert retrieved_prompt is not None
|
|
assert isinstance(retrieved_prompt, opik.ChatPrompt)
|
|
assert retrieved_prompt.name == prompt_name
|
|
assert retrieved_prompt.commit == created_prompt.commit
|
|
assert len(retrieved_prompt.template) == 2
|
|
assert retrieved_prompt.project_name == PROJECT_NAME
|
|
|
|
|
|
def test_get_chat_prompt__string_prompt__prompt_structure_mismatch_error(
|
|
opik_client: opik.Opik,
|
|
):
|
|
"""Test that get_chat_prompt() raises an error for text prompts (type mismatch)."""
|
|
unique_id = str(uuid.uuid4())[-6:]
|
|
prompt_name = f"text-prompt-{unique_id}"
|
|
|
|
# Create a text prompt
|
|
text_prompt = opik_client.create_prompt(
|
|
name=prompt_name,
|
|
prompt="Hello {{name}}",
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
# Try to retrieve it with get_chat_prompt() - should raise an error due to type mismatch
|
|
with pytest.raises(opik.exceptions.PromptTemplateStructureMismatch):
|
|
opik_client.get_chat_prompt(name=prompt_name, project_name=PROJECT_NAME)
|
|
|
|
# Verify the text prompt remains unchanged
|
|
retrieved_prompt = opik_client.get_prompt(
|
|
name=prompt_name, project_name=PROJECT_NAME
|
|
)
|
|
assert retrieved_prompt is not None
|
|
verifiers.verify_prompt_version(
|
|
retrieved_prompt,
|
|
name=prompt_name,
|
|
template=text_prompt.prompt,
|
|
commit=text_prompt.commit,
|
|
prompt_id=text_prompt.__internal_api__prompt_id__,
|
|
version_id=text_prompt.__internal_api__version_id__,
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
|
|
def test_get_chat_prompt_history__chat_prompt__returns_chat_prompts(
|
|
opik_client: opik.Opik,
|
|
):
|
|
"""Test that get_chat_prompt_history() returns ChatPrompt objects for chat prompts."""
|
|
unique_id = str(uuid.uuid4())[-6:]
|
|
prompt_name = f"chat-prompt-history-{unique_id}"
|
|
|
|
# Create multiple versions of a chat prompt
|
|
v1 = opik_client.create_chat_prompt(
|
|
name=prompt_name,
|
|
messages=[{"role": "user", "content": "Version 1"}],
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
v2 = opik_client.create_chat_prompt(
|
|
name=prompt_name,
|
|
messages=[{"role": "user", "content": "Version 2"}],
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
v3 = opik_client.create_chat_prompt(
|
|
name=prompt_name,
|
|
messages=[
|
|
{"role": "system", "content": "System message"},
|
|
{"role": "user", "content": "Version 3"},
|
|
],
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
# Retrieve history
|
|
history = opik_client.get_chat_prompt_history(
|
|
name=prompt_name, project_name=PROJECT_NAME
|
|
)
|
|
|
|
assert len(history) == 3
|
|
assert all(isinstance(p, opik.ChatPrompt) for p in history)
|
|
assert history[0].name == prompt_name
|
|
|
|
# Verify commits are in the history
|
|
commits = {p.commit for p in history}
|
|
assert v1.commit in commits
|
|
assert v2.commit in commits
|
|
assert v3.commit in commits
|
|
|
|
|
|
def test_get_chat_prompt_history__string_prompt__prompt_structure_mismatch_error(
|
|
opik_client: opik.Opik,
|
|
):
|
|
"""Test that get_chat_prompt_history() raises an error for text prompts (type mismatch)."""
|
|
unique_id = str(uuid.uuid4())[-6:]
|
|
prompt_name = f"text-prompt-history-{unique_id}"
|
|
|
|
# Create a text prompt
|
|
text_prompt = opik_client.create_prompt(
|
|
name=prompt_name, prompt="Hello", project_name=PROJECT_NAME
|
|
)
|
|
|
|
# Try to get history with get_chat_prompt_history() - should raise an error due to type mismatch
|
|
with pytest.raises(opik.exceptions.PromptTemplateStructureMismatch):
|
|
opik_client.get_chat_prompt_history(name=prompt_name, project_name=PROJECT_NAME)
|
|
|
|
# Verify the text prompt remains unchanged
|
|
retrieved_prompt = opik_client.get_prompt(
|
|
name=prompt_name, project_name=PROJECT_NAME
|
|
)
|
|
assert retrieved_prompt is not None
|
|
verifiers.verify_prompt_version(
|
|
retrieved_prompt,
|
|
name=prompt_name,
|
|
template=text_prompt.prompt,
|
|
commit=text_prompt.commit,
|
|
prompt_id=text_prompt.__internal_api__prompt_id__,
|
|
version_id=text_prompt.__internal_api__version_id__,
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
|
|
def test_search_prompts__filter_chat_prompts_only(opik_client: opik.Opik):
|
|
"""Test that search_prompts() with template_structure filter returns only ChatPrompt instances."""
|
|
unique_id = str(uuid.uuid4())[-6:]
|
|
|
|
# Create text prompts
|
|
text_prompt_1 = opik_client.create_prompt(
|
|
name=f"text-search-{unique_id}-1",
|
|
prompt="Text prompt 1",
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
text_prompt_2 = opik_client.create_prompt(
|
|
name=f"text-search-{unique_id}-2",
|
|
prompt="Text prompt 2",
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
# Create chat prompts
|
|
chat_prompt_1 = opik_client.create_chat_prompt(
|
|
name=f"chat-search-{unique_id}-1",
|
|
messages=[{"role": "user", "content": "Chat 1"}],
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
chat_prompt_2 = opik_client.create_chat_prompt(
|
|
name=f"chat-search-{unique_id}-2",
|
|
messages=[{"role": "user", "content": "Chat 2"}],
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
# Search for only chat prompts using template_structure filter
|
|
results = opik_client.search_prompts(
|
|
filter_string=f'name contains "{unique_id}" AND template_structure = "chat"',
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
# Should only return chat prompts, not text prompts
|
|
assert len(results) == 2
|
|
assert all(isinstance(p, opik.ChatPrompt) for p in results)
|
|
|
|
result_names = {p.name for p in results}
|
|
assert chat_prompt_1.name in result_names
|
|
assert chat_prompt_2.name in result_names
|
|
assert text_prompt_1.name not in result_names
|
|
assert text_prompt_2.name not in result_names
|
|
|
|
|
|
def test_get_chat_prompt__with_commit__chat_prompt(opik_client: opik.Opik):
|
|
"""Test that get_chat_prompt() with commit works for chat prompts."""
|
|
unique_id = str(uuid.uuid4())[-6:]
|
|
prompt_name = f"chat-prompt-commit-{unique_id}"
|
|
|
|
# Create multiple versions
|
|
v1 = opik_client.create_chat_prompt(
|
|
name=prompt_name,
|
|
messages=[{"role": "user", "content": "Version 1"}],
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
_ = opik_client.create_chat_prompt(
|
|
name=prompt_name,
|
|
messages=[{"role": "user", "content": "Version 2"}],
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
# Retrieve specific version by commit
|
|
retrieved_v1 = opik_client.get_chat_prompt(
|
|
name=prompt_name, commit=v1.commit, project_name=PROJECT_NAME
|
|
)
|
|
|
|
assert retrieved_v1 is not None
|
|
assert isinstance(retrieved_v1, opik.ChatPrompt)
|
|
assert retrieved_v1.commit == v1.commit
|
|
assert retrieved_v1.template[0]["content"] == "Version 1"
|
|
assert retrieved_v1.name == prompt_name
|
|
assert retrieved_v1.project_name == PROJECT_NAME
|
|
|
|
|
|
def test_get_chat_prompt__nonexistent__returns_none(opik_client: opik.Opik):
|
|
"""Test that get_chat_prompt() returns None for non-existent prompts."""
|
|
result = opik_client.get_chat_prompt(
|
|
name="nonexistent-chat-prompt-12345", project_name=opik_client.project_name
|
|
)
|
|
assert result is None
|
|
|
|
|
|
def test_chat_prompt__template_structure_immutable__error(opik_client: opik.Opik):
|
|
"""Test that template_structure is immutable for chat prompts."""
|
|
unique_identifier = str(uuid.uuid4())[-6:]
|
|
prompt_name = f"test-immutable-chat-structure-{unique_identifier}"
|
|
|
|
# Create initial chat prompt
|
|
chat_prompt = opik_client.create_chat_prompt(
|
|
name=prompt_name,
|
|
messages=[
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{"role": "user", "content": "Hello!"},
|
|
],
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
# Verify chat prompt was created
|
|
verifiers.verify_chat_prompt_version(
|
|
chat_prompt,
|
|
name=prompt_name,
|
|
messages=[
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{"role": "user", "content": "Hello!"},
|
|
],
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
# Attempt to create a text prompt version with the same name should fail
|
|
with pytest.raises(opik.exceptions.PromptTemplateStructureMismatch):
|
|
opik_client.create_prompt(
|
|
name=prompt_name,
|
|
prompt="This is a text prompt: {{variable}}",
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
# Verify the original chat prompt remains unchanged
|
|
retrieved_chat_prompt = opik_client.get_chat_prompt(
|
|
name=prompt_name, project_name=PROJECT_NAME
|
|
)
|
|
assert retrieved_chat_prompt is not None
|
|
verifiers.verify_chat_prompt_version(
|
|
retrieved_chat_prompt,
|
|
name=prompt_name,
|
|
messages=chat_prompt.template,
|
|
commit=chat_prompt.commit,
|
|
prompt_id=chat_prompt.__internal_api__prompt_id__,
|
|
version_id=chat_prompt.__internal_api__version_id__,
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
|
|
def test_chat_prompt__invalid_messages__raises_validation_error(opik_client: opik.Opik):
|
|
"""Test that invalid messages raise ValidationError."""
|
|
unique_identifier = str(uuid.uuid4())[-6:]
|
|
prompt_name = f"chat-prompt-invalid-{unique_identifier}"
|
|
|
|
with pytest.raises(opik.exceptions.ValidationError) as exc_info:
|
|
ChatPrompt(
|
|
name=prompt_name,
|
|
messages=[{"role": "invalid", "content": "hello"}],
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
assert "ChatPrompt.__init__" in str(exc_info.value)
|
|
assert "messages[0].role" in str(exc_info.value)
|
|
|
|
|
|
def test_chat_prompt__create_with_tags__happyflow(opik_client: opik.Opik):
|
|
"""Test that create_chat_prompt() accepts tags parameter."""
|
|
unique_identifier = str(uuid.uuid4())[-6:]
|
|
|
|
prompt_name = f"chat-prompt-with-tags-{unique_identifier}"
|
|
messages = [
|
|
{"role": "system", "content": "You are a helpful assistant."},
|
|
{"role": "user", "content": "Hello!"},
|
|
]
|
|
tags = ["chat-tag1", "chat-tag2", "production"]
|
|
|
|
# Create chat prompt with tags
|
|
chat_prompt = opik_client.create_chat_prompt(
|
|
name=prompt_name,
|
|
messages=messages,
|
|
tags=tags,
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
# Verify prompt was created
|
|
verifiers.verify_chat_prompt_version(
|
|
chat_prompt,
|
|
name=prompt_name,
|
|
messages=messages,
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
|
|
# Verify tags were set by searching for the prompt
|
|
filtered_prompts = opik_client.search_prompts(
|
|
filter_string=f'name = "{prompt_name}" AND tags contains "chat-tag1"',
|
|
project_name=PROJECT_NAME,
|
|
)
|
|
assert len(filtered_prompts) == 1
|
|
assert filtered_prompts[0].name == prompt_name
|
|
assert set(filtered_prompts[0].tags) == set(tags)
|
|
assert filtered_prompts[0].project_name == PROJECT_NAME
|