참고소스 수정본
This commit is contained in:
242
참고/instructor-main/instructor/providers/cohere/utils.py
Normal file
242
참고/instructor-main/instructor/providers/cohere/utils.py
Normal 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,
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user