참고소스 수정본

This commit is contained in:
LASTA_DEV01\lasta
2026-05-12 19:40:31 +09:00
parent 0f34a451fc
commit 2e9204243d
8708 changed files with 3259488 additions and 869 deletions

View File

@@ -0,0 +1 @@
"""Provider implementation."""

View File

@@ -0,0 +1,112 @@
from __future__ import annotations
import inspect
from collections.abc import Awaitable
from typing import Any, TypeVar, cast, overload
import cohere
import instructor
from pydantic import BaseModel
from typing_extensions import ParamSpec
T_Model = TypeVar("T_Model", bound=BaseModel)
T_ParamSpec = ParamSpec("T_ParamSpec")
@overload
def from_cohere(
client: cohere.Client,
mode: instructor.Mode = instructor.Mode.COHERE_TOOLS,
**kwargs: Any,
) -> instructor.Instructor: ...
@overload
def from_cohere(
client: cohere.ClientV2,
mode: instructor.Mode = instructor.Mode.COHERE_TOOLS,
**kwargs: Any,
) -> instructor.Instructor: ...
@overload
def from_cohere(
client: cohere.AsyncClient,
mode: instructor.Mode = instructor.Mode.COHERE_JSON_SCHEMA,
**kwargs: Any,
) -> instructor.AsyncInstructor: ...
@overload
def from_cohere(
client: cohere.AsyncClientV2,
mode: instructor.Mode = instructor.Mode.COHERE_JSON_SCHEMA,
**kwargs: Any,
) -> instructor.AsyncInstructor: ...
def from_cohere(
client: cohere.Client | cohere.AsyncClient | cohere.ClientV2 | cohere.AsyncClientV2,
mode: instructor.Mode = instructor.Mode.COHERE_TOOLS,
**kwargs: Any,
):
valid_modes = {
instructor.Mode.COHERE_TOOLS,
instructor.Mode.COHERE_JSON_SCHEMA,
}
if mode not in valid_modes:
from ...core.exceptions import ModeError
raise ModeError(
mode=str(mode), provider="Cohere", valid_modes=[str(m) for m in valid_modes]
)
# Determine if we're dealing with an async client
is_async = isinstance(client, (cohere.AsyncClient, cohere.AsyncClientV2))
if isinstance(client, (cohere.ClientV2, cohere.AsyncClientV2)):
client_version = "v2"
elif isinstance(client, (cohere.Client, cohere.AsyncClient)):
client_version = "v1"
else:
from ...core.exceptions import ClientError
raise ClientError(
f"Client must be an instance of cohere.Client or cohere.AsyncClient or cohere.ClientV2 or cohere.AsyncClientV2. "
f"Got: {type(client).__name__}"
)
kwargs["_cohere_client_version"] = client_version
if is_async:
async def async_wrapper(*args: Any, **call_kwargs: Any):
if call_kwargs.pop("stream", False):
return client.chat_stream(*args, **call_kwargs)
result = client.chat(*args, **call_kwargs)
if inspect.isawaitable(result):
return await cast(Awaitable[Any], result)
return result
return instructor.AsyncInstructor(
client=client,
create=instructor.patch(create=async_wrapper, mode=mode),
provider=instructor.Provider.COHERE,
mode=mode,
**kwargs,
)
else:
def sync_wrapper(*args: Any, **call_kwargs: Any):
if call_kwargs.pop("stream", False):
return client.chat_stream(*args, **call_kwargs)
return client.chat(*args, **call_kwargs)
return instructor.Instructor(
client=client,
create=instructor.patch(create=sync_wrapper, mode=mode),
provider=instructor.Provider.COHERE,
mode=mode,
**kwargs,
)

View File

@@ -0,0 +1,242 @@
"""Cohere-specific utilities.
This module contains utilities specific to the Cohere provider,
including reask functions, response handlers, and message formatting.
"""
from __future__ import annotations
from typing import Any
from ...mode import Mode
def reask_cohere_tools(
kwargs: dict[str, Any],
response: Any, # Replace with actual response type for Cohere
exception: Exception,
):
"""
Handle reask for Cohere tools and JSON schema modes.
Supports both V1 and V2 formats.
V1 kwargs modifications:
- Adds/Modifies: "chat_history" (appends prior message)
- Modifies: "message" (user prompt describing validation errors)
V2 kwargs modifications:
- Modifies: "messages" (appends error correction message)
"""
# Default to marker stored on kwargs (set during client initialization)
client_version = kwargs.get("_cohere_client_version")
# Detect V1 vs V2 response structure and extract text
if hasattr(response, "text"):
client_version = "v1"
response_text = response.text
elif hasattr(response, "message") and hasattr(response.message, "content"):
client_version = "v2"
content_items = response.message.content
response_text = ""
if content_items:
# Find the text content item (skip thinking/other types)
for item in content_items:
if (
hasattr(item, "type")
and item.type == "text"
and hasattr(item, "text")
):
response_text = item.text
break
if not response_text:
response_text = str(response)
else:
# Fallback to string representation
response_text = str(response)
if client_version is None:
if "messages" in kwargs:
client_version = "v2"
elif "chat_history" in kwargs or "message" in kwargs:
client_version = "v1"
# Create the correction message
correction_msg = (
"Correct the following JSON response, based on the errors given below:\n\n"
f"JSON:\n{response_text}\n\nExceptions:\n{exception}"
)
if client_version == "v2":
# V2 format: append to messages list
kwargs["messages"].append({"role": "user", "content": correction_msg})
elif client_version == "v1":
# V1 format: use chat_history and message
message = kwargs.get("message", "")
# Fetch or initialize chat_history in one operation
if "chat_history" in kwargs:
kwargs["chat_history"].append({"role": "user", "message": message})
else:
kwargs["chat_history"] = [{"role": "user", "message": message}]
kwargs["message"] = correction_msg
else:
# Unknown version - raise error for future compatibility
raise ValueError(
f"Unsupported Cohere client version: {client_version}. "
f"Expected 'v1' or 'v2'."
)
return kwargs
def handle_cohere_modes(new_kwargs: dict[str, Any]) -> tuple[None, dict[str, Any]]:
"""
Convert OpenAI-style messages to Cohere format.
Handles both V1 and V2 client formats.
V1 format:
- Removes: "messages"
- Adds: "message" (last user message)
- Adds: "chat_history" (prior messages)
V2 format:
- Keeps: "messages" (compatible with OpenAI format)
Both versions:
- Renames: "model_name" -> "model"
- Removes: "strict"
- Removes: "_cohere_client_version" (internal marker)
"""
new_kwargs = new_kwargs.copy()
client_version = new_kwargs.pop("_cohere_client_version")
if client_version == "v2":
# V2 uses OpenAI-style messages directly - no conversion needed
# Just clean up incompatible fields
if "model_name" in new_kwargs and "model" not in new_kwargs:
new_kwargs["model"] = new_kwargs.pop("model_name")
new_kwargs.pop("strict", None)
elif client_version == "v1":
# V1 needs conversion from OpenAI format to Cohere V1 format
messages = new_kwargs.pop("messages", [])
chat_history = []
for message in messages[:-1]:
chat_history.append( # type: ignore[arg-type]
{
"role": message["role"],
"message": message["content"],
}
)
new_kwargs["message"] = messages[-1]["content"]
new_kwargs["chat_history"] = chat_history
if "model_name" in new_kwargs and "model" not in new_kwargs:
new_kwargs["model"] = new_kwargs.pop("model_name")
new_kwargs.pop("strict", None)
else:
# Unknown version - raise error for future compatibility
raise ValueError(
f"Unsupported Cohere client version: {client_version}. "
f"Expected 'v1' or 'v2'."
)
return None, new_kwargs
def handle_cohere_json_schema(
response_model: type[Any] | None, new_kwargs: dict[str, Any]
) -> tuple[type[Any] | None, dict[str, Any]]:
"""
Handle Cohere JSON schema mode.
When response_model is None:
- Converts messages from OpenAI format to Cohere format (message + chat_history)
- No schema is added to the request
When response_model is provided:
- Converts messages from OpenAI format to Cohere format
- Adds the model's JSON schema to response_format
Kwargs modifications:
- Removes: "messages" (converted to message + chat_history)
- Adds: "message" (last message content)
- Adds: "chat_history" (all messages except last)
- Modifies: "model" (if "model_name" exists, renames to "model")
- Removes: "strict"
- Adds: "response_format" (with JSON schema) - only when response_model provided
"""
if response_model is None:
# Just handle message conversion
return handle_cohere_modes(new_kwargs)
new_kwargs["response_format"] = {
"type": "json_object",
"schema": response_model.model_json_schema(),
}
_, new_kwargs = handle_cohere_modes(new_kwargs)
return response_model, new_kwargs
def handle_cohere_tools(
response_model: type[Any] | None, new_kwargs: dict[str, Any]
) -> tuple[type[Any] | None, dict[str, Any]]:
"""
Handle Cohere tools mode.
When response_model is None:
- Converts messages from OpenAI format to Cohere format (message + chat_history for V1, messages for V2)
- No tools or schema instructions are added
- Allows for unstructured responses from Cohere
When response_model is provided:
- Converts messages from OpenAI format to Cohere format
- Prepends extraction instructions to the chat history (V1) or messages (V2)
- Includes the model's JSON schema in the instructions
- The model is instructed to extract a valid object matching the schema
Kwargs modifications:
- All modifications from handle_cohere_modes (message format conversion)
- Modifies: "chat_history" (V1) or "messages" (V2) to prepend extraction instruction - only when response_model provided
"""
if response_model is None:
# Just handle message conversion
return handle_cohere_modes(new_kwargs)
_, new_kwargs = handle_cohere_modes(new_kwargs)
instruction = f"""\
Extract a valid {response_model.__name__} object based on the chat history and the json schema below.
{response_model.model_json_schema()}
The JSON schema was obtained by running:
```python
schema = {response_model.__name__}.model_json_schema()
```
The output must be a valid JSON object that `{response_model.__name__}.model_validate_json()` can successfully parse.
Respond with JSON only. Do not include code fences, markdown, or extra text.
"""
# Check client version explicitly (marker already removed by handle_cohere_modes)
# Use presence of messages vs chat_history as indicator since marker is already consumed
if "messages" in new_kwargs:
# V2 format: prepend to messages
new_kwargs["messages"].insert(0, {"role": "user", "content": instruction})
else:
# V1 format: prepend to chat_history
new_kwargs["chat_history"] = [
{"role": "user", "message": instruction}
] + new_kwargs["chat_history"]
return response_model, new_kwargs
# Handler registry for Cohere
COHERE_HANDLERS = {
Mode.COHERE_TOOLS: {
"reask": reask_cohere_tools,
"response": handle_cohere_tools,
},
Mode.COHERE_JSON_SCHEMA: {
"reask": reask_cohere_tools,
"response": handle_cohere_json_schema,
},
}