97e91a83f3
Ruff / Ruff (push) Has been cancelled
Test / Core Tests (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.10) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.11) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.12) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.13) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.9) (push) Has been cancelled
Test / Full Coverage (Python 3.11) (push) Has been cancelled
Test / Core Provider Tests (OpenAI) (push) Has been cancelled
Test / Core Provider Tests (Anthropic) (push) Has been cancelled
Test / Core Provider Tests (Google) (push) Has been cancelled
Test / Core Provider Tests (Other) (push) Has been cancelled
Test / Anthropic Tests (push) Has been cancelled
Test / Gemini Tests (push) Has been cancelled
Test / Google GenAI Tests (push) Has been cancelled
Test / Vertex AI Tests (push) Has been cancelled
Test / OpenAI Tests (push) Has been cancelled
Test / Writer Tests (push) Has been cancelled
Test / Auto Client Tests (push) Has been cancelled
ty / type-check (push) Has been cancelled
148 lines
4.7 KiB
Python
148 lines
4.7 KiB
Python
# type: ignore[all]
|
|
from __future__ import annotations
|
|
from typing import Any
|
|
from textwrap import dedent
|
|
from instructor.v2.core.mode import Mode
|
|
from instructor.v2.core.providers import Provider, provider_from_mode
|
|
from jinja2.sandbox import SandboxedEnvironment
|
|
|
|
|
|
def apply_template(text: str, context: dict[str, Any]) -> str:
|
|
"""Apply Jinja2 template to the given text."""
|
|
return dedent(SandboxedEnvironment().from_string(text).render(**context))
|
|
|
|
|
|
def process_message(
|
|
message: dict[str, Any], context: dict[str, Any], provider: Provider
|
|
) -> dict[str, Any]:
|
|
"""Process a single message, applying templates to its content."""
|
|
if provider == Provider.GENAI:
|
|
from instructor.v2.providers.genai.templating import (
|
|
process_message as process_genai_message,
|
|
)
|
|
|
|
return process_genai_message(message, context, apply_template)
|
|
|
|
# VertexAI Support
|
|
if (
|
|
hasattr(message, "parts")
|
|
and isinstance(message.parts, list)
|
|
and len(message.parts) > 0
|
|
and not isinstance(message.parts[0], str)
|
|
):
|
|
from instructor.v2.providers.vertexai.templating import (
|
|
process_message as process_vertexai_message,
|
|
)
|
|
|
|
return process_vertexai_message(message, context, apply_template)
|
|
|
|
# OpenAI format
|
|
if isinstance(message.get("content"), str):
|
|
from instructor.v2.providers.openai.templating import (
|
|
process_message as process_openai_message,
|
|
)
|
|
|
|
return process_openai_message(message, context, apply_template)
|
|
|
|
# Anthropic format
|
|
if isinstance(message.get("content"), list):
|
|
from instructor.v2.providers.anthropic.templating import (
|
|
process_message as process_anthropic_message,
|
|
)
|
|
|
|
return process_anthropic_message(message, context, apply_template)
|
|
|
|
# Gemini Support
|
|
if isinstance(message.get("parts"), list):
|
|
from instructor.v2.providers.gemini.templating import (
|
|
process_message as process_gemini_message,
|
|
)
|
|
|
|
return process_gemini_message(message, context, apply_template)
|
|
|
|
# Cohere format
|
|
if isinstance(message.get("message"), str):
|
|
from instructor.v2.providers.cohere.templating import (
|
|
process_message as process_cohere_message,
|
|
)
|
|
|
|
return process_cohere_message(message, context, apply_template)
|
|
|
|
return message
|
|
|
|
|
|
def _copy_message_for_templating(message: Any) -> Any:
|
|
if not isinstance(message, dict):
|
|
return message
|
|
|
|
copied_message = message.copy()
|
|
for field in ("content", "parts"):
|
|
parts = copied_message.get(field)
|
|
if isinstance(parts, list):
|
|
copied_message[field] = [
|
|
part.copy() if isinstance(part, dict) else part for part in parts
|
|
]
|
|
return copied_message
|
|
|
|
|
|
def handle_templating(
|
|
kwargs: dict[str, Any],
|
|
mode: Mode, # noqa: ARG001
|
|
provider: Provider | dict[str, Any] | None = None,
|
|
context: dict[str, Any] | None = None,
|
|
) -> dict[str, Any]:
|
|
"""
|
|
Handle templating for messages using the provided context.
|
|
|
|
This function processes messages, applying Jinja2 templating to their content
|
|
using the provided context. It supports various message formats including
|
|
OpenAI, Anthropic, Cohere, VertexAI, and Gemini.
|
|
|
|
Args:
|
|
kwargs (Dict[str, Any]): Keyword arguments being passed to the create method.
|
|
context (Dict[str, Any] | None, optional): Variables to use in templating. Defaults to None.
|
|
|
|
Returns:
|
|
Dict[str, Any]: The processed kwargs with templated content.
|
|
|
|
Raises:
|
|
ValueError: If no recognized message format is found in kwargs.
|
|
"""
|
|
if context is None and isinstance(provider, dict):
|
|
context = provider
|
|
provider = None
|
|
|
|
if not context:
|
|
return kwargs
|
|
|
|
if not isinstance(provider, Provider):
|
|
provider = provider_from_mode(mode, Provider.OPENAI)
|
|
|
|
new_kwargs = kwargs.copy()
|
|
|
|
# Handle Cohere's message field
|
|
if "message" in new_kwargs:
|
|
new_kwargs["message"] = apply_template(new_kwargs["message"], context)
|
|
new_kwargs["chat_history"] = [
|
|
process_message(_copy_message_for_templating(message), context, provider)
|
|
for message in new_kwargs.get("chat_history", [])
|
|
]
|
|
|
|
return new_kwargs
|
|
|
|
if isinstance(new_kwargs, list):
|
|
return new_kwargs
|
|
|
|
message_key = "messages" if new_kwargs.get("messages") else "contents"
|
|
messages = new_kwargs.get(message_key)
|
|
|
|
if not messages:
|
|
return new_kwargs
|
|
|
|
new_kwargs[message_key] = [
|
|
process_message(_copy_message_for_templating(message), context, provider)
|
|
for message in messages
|
|
]
|
|
|
|
return new_kwargs
|