-
-
Notifications
You must be signed in to change notification settings - Fork 11k
[gpt-oss] Harmony changes with container tool support #23386
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,11 +16,13 @@ | |
| from openai.types.responses.response_reasoning_item import ( | ||
| Content as ResponseReasoningTextContent) | ||
| from openai.types.responses.tool import Tool | ||
| from openai_harmony import (Author, Conversation, DeveloperContent, | ||
| HarmonyEncodingName, Message, ReasoningEffort, | ||
| Role, StreamableParser, SystemContent, TextContent, | ||
| ToolDescription, load_harmony_encoding) | ||
| from openai_harmony import (Author, ChannelConfig, Conversation, | ||
| DeveloperContent, HarmonyEncodingName, Message, | ||
| ReasoningEffort, Role, StreamableParser, | ||
| SystemContent, TextContent, ToolDescription, | ||
| load_harmony_encoding) | ||
|
|
||
| from vllm import envs | ||
| from vllm.entrypoints.openai.protocol import (ChatCompletionToolsParam, | ||
| ResponseInputOutputItem) | ||
| from vllm.utils import random_uuid | ||
|
|
@@ -33,6 +35,20 @@ | |
|
|
||
| _harmony_encoding = None | ||
|
|
||
| # Builtin tools that should be included in the system message when | ||
| # they are available and requested by the user. | ||
| # Tool args are provided by MCP tool descriptions. Output | ||
| # of the tools are stringified. | ||
| BUILTIN_TOOLS = { | ||
| "web_search_preview", | ||
|
||
| "code_interpreter", | ||
| "container", | ||
| } | ||
|
|
||
|
|
||
| def has_custom_tools(tool_types: list[str]) -> bool: | ||
| return not set(tool_types).issubset(BUILTIN_TOOLS) | ||
|
|
||
|
|
||
| def get_encoding(): | ||
| global _harmony_encoding | ||
|
|
@@ -48,10 +64,19 @@ def get_system_message( | |
| start_date: Optional[str] = None, | ||
| browser_description: Optional[str] = None, | ||
| python_description: Optional[str] = None, | ||
| container_description: Optional[str] = None, | ||
| instructions: Optional[str] = None, | ||
| with_custom_tools: bool = False, | ||
|
||
| ) -> Message: | ||
| sys_msg_content = SystemContent.new() | ||
| if model_identity is not None: | ||
| sys_msg_content = sys_msg_content.with_model_identity(model_identity) | ||
| if (instructions is not None | ||
| and envs.VLLM_GPT_OSS_HARMONY_SYSTEM_INSTRUCTIONS): | ||
| current_identity = sys_msg_content.model_identity | ||
| new_identity = (f'{current_identity}\n{instructions}' | ||
| if current_identity else instructions) | ||
| sys_msg_content = sys_msg_content.with_model_identity(new_identity) | ||
| if reasoning_effort is not None: | ||
| sys_msg_content = sys_msg_content.with_reasoning_effort( | ||
| REASONING_EFFORT[reasoning_effort]) | ||
|
|
@@ -63,6 +88,14 @@ def get_system_message( | |
| sys_msg_content = sys_msg_content.with_tools(browser_description) | ||
| if python_description is not None: | ||
| sys_msg_content = sys_msg_content.with_tools(python_description) | ||
| if container_description is not None: | ||
| sys_msg_content = sys_msg_content.with_tools(container_description) | ||
| if not with_custom_tools: | ||
|
||
| channel_config = sys_msg_content.channel_config | ||
| invalid_channel = "commentary" | ||
| new_config = ChannelConfig.require_channels( | ||
| [c for c in channel_config.valid_channels if c != invalid_channel]) | ||
| sys_msg_content = sys_msg_content.with_channel_config(new_config) | ||
| sys_msg = Message.from_role_and_content(Role.SYSTEM, sys_msg_content) | ||
| return sys_msg | ||
|
|
||
|
|
@@ -86,14 +119,17 @@ def get_developer_message( | |
| tools: Optional[list[Union[Tool, ChatCompletionToolsParam]]] = None, | ||
| ) -> Message: | ||
| dev_msg_content = DeveloperContent.new() | ||
| if instructions is not None: | ||
| if (instructions is not None | ||
| and not envs.VLLM_GPT_OSS_HARMONY_SYSTEM_INSTRUCTIONS): | ||
| dev_msg_content = dev_msg_content.with_instructions(instructions) | ||
| if tools is not None: | ||
| function_tools: list[Union[Tool, ChatCompletionToolsParam]] = [] | ||
| for tool in tools: | ||
| if tool.type in ("web_search_preview", "code_interpreter"): | ||
| if tool.type in ("web_search_preview", "code_interpreter", | ||
| "container"): | ||
| # These are built-in tools that are added to the system message. | ||
| pass | ||
|
|
||
| elif tool.type == "function": | ||
| function_tools.append(tool) | ||
| else: | ||
|
|
@@ -136,6 +172,8 @@ def parse_response_input( | |
| TextContent(text=text_prefix + c["text"]) for c in content | ||
| ] | ||
| msg = Message.from_role_and_contents(role, contents) | ||
| if role == "assistant": | ||
| msg = msg.with_channel("final") | ||
| elif response_msg["type"] == "function_call_output": | ||
| call_id = response_msg["call_id"] | ||
| call_response: Optional[ResponseFunctionToolCall] = None | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -86,7 +86,8 @@ def get_tool_description(self, | |
| pass | ||
|
|
||
| @abstractmethod | ||
| def new_session(self, tool_name: str) -> AbstractAsyncContextManager[Any]: | ||
| def new_session(self, tool_name: str, | ||
| session_id: str) -> AbstractAsyncContextManager[Any]: | ||
| """ | ||
| Create a session for the tool. | ||
| """ | ||
|
|
@@ -124,7 +125,8 @@ async def add_tool_server(self, server_url: str): | |
| description=tool.description, | ||
| parameters=tool.inputSchema) | ||
| for tool in list_tools_response.tools | ||
| ]) | ||
| ], | ||
| ) | ||
| self.harmony_tool_descriptions[tool_from_mcp.name] = tool_from_mcp | ||
| if tool_from_mcp.name not in self.urls: | ||
| self.urls[tool_from_mcp.name] = url | ||
|
|
@@ -142,14 +144,16 @@ def get_tool_description(self, tool_name: str): | |
| return self.harmony_tool_descriptions.get(tool_name) | ||
|
|
||
| @asynccontextmanager | ||
| async def new_session(self, tool_name: str): | ||
| async def new_session(self, tool_name: str, session_id: str): | ||
| from mcp import ClientSession | ||
| from mcp.client.sse import sse_client | ||
| url = self.urls.get(tool_name) | ||
| headers = {"x-session-id": session_id} | ||
| if not url: | ||
| raise KeyError(f"Tool '{tool_name}' is not supported") | ||
| async with sse_client(url=url) as streams, ClientSession( | ||
| *streams) as session: | ||
| async with sse_client(url=url, | ||
| headers=headers) as streams, ClientSession( | ||
| *streams) as session: | ||
| await session.initialize() | ||
| yield session | ||
|
||
|
|
||
|
|
@@ -182,7 +186,7 @@ def get_tool_description(self, | |
| raise ValueError(f"Unknown tool {tool_name}") | ||
|
|
||
| @asynccontextmanager | ||
| async def new_session(self, tool_name: str): | ||
| async def new_session(self, tool_name: str, session_id: str): | ||
| if tool_name not in self.tools: | ||
| raise KeyError(f"Tool '{tool_name}' is not supported") | ||
| yield self.tools[tool_name] | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is
containertool?