Files
AI/참고/instructor-main/instructor/templating.py

143 lines
4.3 KiB
Python
Raw Normal View History

2026-05-12 19:40:31 +09:00
# type: ignore[all]
from __future__ import annotations
from typing import Any
from textwrap import dedent
from instructor.mode import 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], mode: Mode
) -> dict[str, Any]:
"""Process a single message, applying templates to its content."""
if mode in {Mode.GENAI_TOOLS, Mode.GENAI_STRUCTURED_OUTPUTS}:
from google.genai import types
return types.Content(
role=message.role,
parts=[
(
types.Part.from_text(text=apply_template(part.text, context))
if isinstance(getattr(part, "text", None), str)
else part
)
for part in message.parts
],
)
# VertexAI Support
if (
hasattr(message, "parts")
and isinstance(message.parts, list)
and len(message.parts) > 0
and not isinstance(message.parts[0], str)
):
import vertexai.generative_models as gm
return gm.Content(
role=message.role,
parts=[
(
gm.Part.from_text(apply_template(part.text, context))
if isinstance(getattr(part, "text", None), str)
else part
)
for part in message.parts
],
)
# OpenAI format
if isinstance(message.get("content"), str):
message["content"] = apply_template(message["content"], context)
return message
# Anthropic format
if isinstance(message.get("content"), list):
for part in message["content"]:
if (
isinstance(part, dict)
and part.get("type") == "text"
and isinstance(part.get("text"), str)
):
part["text"] = apply_template(part["text"], context)
return message
# Gemini Support
if isinstance(message.get("parts"), list):
message["parts"] = [
apply_template(part, context) if isinstance(part, str) else part
for part in message["parts"]
]
return message
# Cohere format
if isinstance(message.get("message"), str):
message["message"] = apply_template(message["message"], context)
return message
return message
def handle_templating(
kwargs: dict[str, Any], mode: Mode, 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 not context:
return kwargs
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(message, context, mode)
for message in new_kwargs["chat_history"]
]
return new_kwargs
if isinstance(new_kwargs, list):
messages = new_kwargs
if not messages:
return new_kwargs
elif isinstance(new_kwargs, dict):
messages = new_kwargs.get("messages") or new_kwargs.get("contents")
if not messages:
return new_kwargs
if "messages" in new_kwargs:
new_kwargs["messages"] = [
process_message(message, context, mode) for message in messages
]
elif "contents" in new_kwargs:
new_kwargs["contents"] = [
process_message(content, context, mode)
for content in new_kwargs["contents"]
]
return new_kwargs