massgen 0.0.3__py3-none-any.whl → 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of massgen might be problematic. Click here for more details.
- massgen/__init__.py +142 -8
- massgen/adapters/__init__.py +29 -0
- massgen/adapters/ag2_adapter.py +483 -0
- massgen/adapters/base.py +183 -0
- massgen/adapters/tests/__init__.py +0 -0
- massgen/adapters/tests/test_ag2_adapter.py +439 -0
- massgen/adapters/tests/test_agent_adapter.py +128 -0
- massgen/adapters/utils/__init__.py +2 -0
- massgen/adapters/utils/ag2_utils.py +236 -0
- massgen/adapters/utils/tests/__init__.py +0 -0
- massgen/adapters/utils/tests/test_ag2_utils.py +138 -0
- massgen/agent_config.py +329 -55
- massgen/api_params_handler/__init__.py +10 -0
- massgen/api_params_handler/_api_params_handler_base.py +99 -0
- massgen/api_params_handler/_chat_completions_api_params_handler.py +176 -0
- massgen/api_params_handler/_claude_api_params_handler.py +113 -0
- massgen/api_params_handler/_response_api_params_handler.py +130 -0
- massgen/backend/__init__.py +39 -4
- massgen/backend/azure_openai.py +385 -0
- massgen/backend/base.py +341 -69
- massgen/backend/base_with_mcp.py +1102 -0
- massgen/backend/capabilities.py +386 -0
- massgen/backend/chat_completions.py +577 -130
- massgen/backend/claude.py +1033 -537
- massgen/backend/claude_code.py +1203 -0
- massgen/backend/cli_base.py +209 -0
- massgen/backend/docs/BACKEND_ARCHITECTURE.md +126 -0
- massgen/backend/{CLAUDE_API_RESEARCH.md → docs/CLAUDE_API_RESEARCH.md} +18 -18
- massgen/backend/{GEMINI_API_DOCUMENTATION.md → docs/GEMINI_API_DOCUMENTATION.md} +9 -9
- massgen/backend/docs/Gemini MCP Integration Analysis.md +1050 -0
- massgen/backend/docs/MCP_IMPLEMENTATION_CLAUDE_BACKEND.md +177 -0
- massgen/backend/docs/MCP_INTEGRATION_RESPONSE_BACKEND.md +352 -0
- massgen/backend/docs/OPENAI_GPT5_MODELS.md +211 -0
- massgen/backend/{OPENAI_RESPONSES_API_FORMAT.md → docs/OPENAI_RESPONSE_API_TOOL_CALLS.md} +3 -3
- massgen/backend/docs/OPENAI_response_streaming.md +20654 -0
- massgen/backend/docs/inference_backend.md +257 -0
- massgen/backend/docs/permissions_and_context_files.md +1085 -0
- massgen/backend/external.py +126 -0
- massgen/backend/gemini.py +1850 -241
- massgen/backend/grok.py +40 -156
- massgen/backend/inference.py +156 -0
- massgen/backend/lmstudio.py +171 -0
- massgen/backend/response.py +1095 -322
- massgen/chat_agent.py +131 -113
- massgen/cli.py +1560 -275
- massgen/config_builder.py +2396 -0
- massgen/configs/BACKEND_CONFIGURATION.md +458 -0
- massgen/configs/README.md +559 -216
- massgen/configs/ag2/ag2_case_study.yaml +27 -0
- massgen/configs/ag2/ag2_coder.yaml +34 -0
- massgen/configs/ag2/ag2_coder_case_study.yaml +36 -0
- massgen/configs/ag2/ag2_gemini.yaml +27 -0
- massgen/configs/ag2/ag2_groupchat.yaml +108 -0
- massgen/configs/ag2/ag2_groupchat_gpt.yaml +118 -0
- massgen/configs/ag2/ag2_single_agent.yaml +21 -0
- massgen/configs/basic/multi/fast_timeout_example.yaml +37 -0
- massgen/configs/basic/multi/gemini_4o_claude.yaml +31 -0
- massgen/configs/basic/multi/gemini_gpt5nano_claude.yaml +36 -0
- massgen/configs/{gemini_4o_claude.yaml → basic/multi/geminicode_4o_claude.yaml} +3 -3
- massgen/configs/basic/multi/geminicode_gpt5nano_claude.yaml +36 -0
- massgen/configs/basic/multi/glm_gemini_claude.yaml +25 -0
- massgen/configs/basic/multi/gpt4o_audio_generation.yaml +30 -0
- massgen/configs/basic/multi/gpt4o_image_generation.yaml +31 -0
- massgen/configs/basic/multi/gpt5nano_glm_qwen.yaml +26 -0
- massgen/configs/basic/multi/gpt5nano_image_understanding.yaml +26 -0
- massgen/configs/{three_agents_default.yaml → basic/multi/three_agents_default.yaml} +8 -4
- massgen/configs/basic/multi/three_agents_opensource.yaml +27 -0
- massgen/configs/basic/multi/three_agents_vllm.yaml +20 -0
- massgen/configs/basic/multi/two_agents_gemini.yaml +19 -0
- massgen/configs/{two_agents.yaml → basic/multi/two_agents_gpt5.yaml} +14 -6
- massgen/configs/basic/multi/two_agents_opensource_lmstudio.yaml +31 -0
- massgen/configs/basic/multi/two_qwen_vllm_sglang.yaml +28 -0
- massgen/configs/{single_agent.yaml → basic/single/single_agent.yaml} +1 -1
- massgen/configs/{single_flash2.5.yaml → basic/single/single_flash2.5.yaml} +1 -2
- massgen/configs/basic/single/single_gemini2.5pro.yaml +16 -0
- massgen/configs/basic/single/single_gpt4o_audio_generation.yaml +22 -0
- massgen/configs/basic/single/single_gpt4o_image_generation.yaml +22 -0
- massgen/configs/basic/single/single_gpt4o_video_generation.yaml +24 -0
- massgen/configs/basic/single/single_gpt5nano.yaml +20 -0
- massgen/configs/basic/single/single_gpt5nano_file_search.yaml +18 -0
- massgen/configs/basic/single/single_gpt5nano_image_understanding.yaml +17 -0
- massgen/configs/basic/single/single_gptoss120b.yaml +15 -0
- massgen/configs/basic/single/single_openrouter_audio_understanding.yaml +15 -0
- massgen/configs/basic/single/single_qwen_video_understanding.yaml +15 -0
- massgen/configs/debug/code_execution/command_filtering_blacklist.yaml +29 -0
- massgen/configs/debug/code_execution/command_filtering_whitelist.yaml +28 -0
- massgen/configs/debug/code_execution/docker_verification.yaml +29 -0
- massgen/configs/debug/skip_coordination_test.yaml +27 -0
- massgen/configs/debug/test_sdk_migration.yaml +17 -0
- massgen/configs/docs/DISCORD_MCP_SETUP.md +208 -0
- massgen/configs/docs/TWITTER_MCP_ENESCINAR_SETUP.md +82 -0
- massgen/configs/providers/azure/azure_openai_multi.yaml +21 -0
- massgen/configs/providers/azure/azure_openai_single.yaml +19 -0
- massgen/configs/providers/claude/claude.yaml +14 -0
- massgen/configs/providers/gemini/gemini_gpt5nano.yaml +28 -0
- massgen/configs/providers/local/lmstudio.yaml +11 -0
- massgen/configs/providers/openai/gpt5.yaml +46 -0
- massgen/configs/providers/openai/gpt5_nano.yaml +46 -0
- massgen/configs/providers/others/grok_single_agent.yaml +19 -0
- massgen/configs/providers/others/zai_coding_team.yaml +108 -0
- massgen/configs/providers/others/zai_glm45.yaml +12 -0
- massgen/configs/{creative_team.yaml → teams/creative/creative_team.yaml} +16 -6
- massgen/configs/{travel_planning.yaml → teams/creative/travel_planning.yaml} +16 -6
- massgen/configs/{news_analysis.yaml → teams/research/news_analysis.yaml} +16 -6
- massgen/configs/{research_team.yaml → teams/research/research_team.yaml} +15 -7
- massgen/configs/{technical_analysis.yaml → teams/research/technical_analysis.yaml} +16 -6
- massgen/configs/tools/code-execution/basic_command_execution.yaml +25 -0
- massgen/configs/tools/code-execution/code_execution_use_case_simple.yaml +41 -0
- massgen/configs/tools/code-execution/docker_claude_code.yaml +32 -0
- massgen/configs/tools/code-execution/docker_multi_agent.yaml +32 -0
- massgen/configs/tools/code-execution/docker_simple.yaml +29 -0
- massgen/configs/tools/code-execution/docker_with_resource_limits.yaml +32 -0
- massgen/configs/tools/code-execution/multi_agent_playwright_automation.yaml +57 -0
- massgen/configs/tools/filesystem/cc_gpt5_gemini_filesystem.yaml +34 -0
- massgen/configs/tools/filesystem/claude_code_context_sharing.yaml +68 -0
- massgen/configs/tools/filesystem/claude_code_flash2.5.yaml +43 -0
- massgen/configs/tools/filesystem/claude_code_flash2.5_gptoss.yaml +49 -0
- massgen/configs/tools/filesystem/claude_code_gpt5nano.yaml +31 -0
- massgen/configs/tools/filesystem/claude_code_single.yaml +40 -0
- massgen/configs/tools/filesystem/fs_permissions_test.yaml +87 -0
- massgen/configs/tools/filesystem/gemini_gemini_workspace_cleanup.yaml +54 -0
- massgen/configs/tools/filesystem/gemini_gpt5_filesystem_casestudy.yaml +30 -0
- massgen/configs/tools/filesystem/gemini_gpt5nano_file_context_path.yaml +43 -0
- massgen/configs/tools/filesystem/gemini_gpt5nano_protected_paths.yaml +45 -0
- massgen/configs/tools/filesystem/gpt5mini_cc_fs_context_path.yaml +31 -0
- massgen/configs/tools/filesystem/grok4_gpt5_gemini_filesystem.yaml +32 -0
- massgen/configs/tools/filesystem/multiturn/grok4_gpt5_claude_code_filesystem_multiturn.yaml +58 -0
- massgen/configs/tools/filesystem/multiturn/grok4_gpt5_gemini_filesystem_multiturn.yaml +58 -0
- massgen/configs/tools/filesystem/multiturn/two_claude_code_filesystem_multiturn.yaml +47 -0
- massgen/configs/tools/filesystem/multiturn/two_gemini_flash_filesystem_multiturn.yaml +48 -0
- massgen/configs/tools/mcp/claude_code_discord_mcp_example.yaml +27 -0
- massgen/configs/tools/mcp/claude_code_simple_mcp.yaml +35 -0
- massgen/configs/tools/mcp/claude_code_twitter_mcp_example.yaml +32 -0
- massgen/configs/tools/mcp/claude_mcp_example.yaml +24 -0
- massgen/configs/tools/mcp/claude_mcp_test.yaml +27 -0
- massgen/configs/tools/mcp/five_agents_travel_mcp_test.yaml +157 -0
- massgen/configs/tools/mcp/five_agents_weather_mcp_test.yaml +103 -0
- massgen/configs/tools/mcp/gemini_mcp_example.yaml +24 -0
- massgen/configs/tools/mcp/gemini_mcp_filesystem_test.yaml +23 -0
- massgen/configs/tools/mcp/gemini_mcp_filesystem_test_sharing.yaml +23 -0
- massgen/configs/tools/mcp/gemini_mcp_filesystem_test_single_agent.yaml +17 -0
- massgen/configs/tools/mcp/gemini_mcp_filesystem_test_with_claude_code.yaml +24 -0
- massgen/configs/tools/mcp/gemini_mcp_test.yaml +27 -0
- massgen/configs/tools/mcp/gemini_notion_mcp.yaml +52 -0
- massgen/configs/tools/mcp/gpt5_nano_mcp_example.yaml +24 -0
- massgen/configs/tools/mcp/gpt5_nano_mcp_test.yaml +27 -0
- massgen/configs/tools/mcp/gpt5mini_claude_code_discord_mcp_example.yaml +38 -0
- massgen/configs/tools/mcp/gpt_oss_mcp_example.yaml +25 -0
- massgen/configs/tools/mcp/gpt_oss_mcp_test.yaml +28 -0
- massgen/configs/tools/mcp/grok3_mini_mcp_example.yaml +24 -0
- massgen/configs/tools/mcp/grok3_mini_mcp_test.yaml +27 -0
- massgen/configs/tools/mcp/multimcp_gemini.yaml +111 -0
- massgen/configs/tools/mcp/qwen_api_mcp_example.yaml +25 -0
- massgen/configs/tools/mcp/qwen_api_mcp_test.yaml +28 -0
- massgen/configs/tools/mcp/qwen_local_mcp_example.yaml +24 -0
- massgen/configs/tools/mcp/qwen_local_mcp_test.yaml +27 -0
- massgen/configs/tools/planning/five_agents_discord_mcp_planning_mode.yaml +140 -0
- massgen/configs/tools/planning/five_agents_filesystem_mcp_planning_mode.yaml +151 -0
- massgen/configs/tools/planning/five_agents_notion_mcp_planning_mode.yaml +151 -0
- massgen/configs/tools/planning/five_agents_twitter_mcp_planning_mode.yaml +155 -0
- massgen/configs/tools/planning/gpt5_mini_case_study_mcp_planning_mode.yaml +73 -0
- massgen/configs/tools/web-search/claude_streamable_http_test.yaml +43 -0
- massgen/configs/tools/web-search/gemini_streamable_http_test.yaml +43 -0
- massgen/configs/tools/web-search/gpt5_mini_streamable_http_test.yaml +43 -0
- massgen/configs/tools/web-search/gpt_oss_streamable_http_test.yaml +44 -0
- massgen/configs/tools/web-search/grok3_mini_streamable_http_test.yaml +43 -0
- massgen/configs/tools/web-search/qwen_api_streamable_http_test.yaml +44 -0
- massgen/configs/tools/web-search/qwen_local_streamable_http_test.yaml +43 -0
- massgen/coordination_tracker.py +708 -0
- massgen/docker/README.md +462 -0
- massgen/filesystem_manager/__init__.py +21 -0
- massgen/filesystem_manager/_base.py +9 -0
- massgen/filesystem_manager/_code_execution_server.py +545 -0
- massgen/filesystem_manager/_docker_manager.py +477 -0
- massgen/filesystem_manager/_file_operation_tracker.py +248 -0
- massgen/filesystem_manager/_filesystem_manager.py +813 -0
- massgen/filesystem_manager/_path_permission_manager.py +1261 -0
- massgen/filesystem_manager/_workspace_tools_server.py +1815 -0
- massgen/formatter/__init__.py +10 -0
- massgen/formatter/_chat_completions_formatter.py +284 -0
- massgen/formatter/_claude_formatter.py +235 -0
- massgen/formatter/_formatter_base.py +156 -0
- massgen/formatter/_response_formatter.py +263 -0
- massgen/frontend/__init__.py +1 -2
- massgen/frontend/coordination_ui.py +471 -286
- massgen/frontend/displays/base_display.py +56 -11
- massgen/frontend/displays/create_coordination_table.py +1956 -0
- massgen/frontend/displays/rich_terminal_display.py +1259 -619
- massgen/frontend/displays/simple_display.py +9 -4
- massgen/frontend/displays/terminal_display.py +27 -68
- massgen/logger_config.py +681 -0
- massgen/mcp_tools/README.md +232 -0
- massgen/mcp_tools/__init__.py +105 -0
- massgen/mcp_tools/backend_utils.py +1035 -0
- massgen/mcp_tools/circuit_breaker.py +195 -0
- massgen/mcp_tools/client.py +894 -0
- massgen/mcp_tools/config_validator.py +138 -0
- massgen/mcp_tools/docs/circuit_breaker.md +646 -0
- massgen/mcp_tools/docs/client.md +950 -0
- massgen/mcp_tools/docs/config_validator.md +478 -0
- massgen/mcp_tools/docs/exceptions.md +1165 -0
- massgen/mcp_tools/docs/security.md +854 -0
- massgen/mcp_tools/exceptions.py +338 -0
- massgen/mcp_tools/hooks.py +212 -0
- massgen/mcp_tools/security.py +780 -0
- massgen/message_templates.py +342 -64
- massgen/orchestrator.py +1515 -241
- massgen/stream_chunk/__init__.py +35 -0
- massgen/stream_chunk/base.py +92 -0
- massgen/stream_chunk/multimodal.py +237 -0
- massgen/stream_chunk/text.py +162 -0
- massgen/tests/mcp_test_server.py +150 -0
- massgen/tests/multi_turn_conversation_design.md +0 -8
- massgen/tests/test_azure_openai_backend.py +156 -0
- massgen/tests/test_backend_capabilities.py +262 -0
- massgen/tests/test_backend_event_loop_all.py +179 -0
- massgen/tests/test_chat_completions_refactor.py +142 -0
- massgen/tests/test_claude_backend.py +15 -28
- massgen/tests/test_claude_code.py +268 -0
- massgen/tests/test_claude_code_context_sharing.py +233 -0
- massgen/tests/test_claude_code_orchestrator.py +175 -0
- massgen/tests/test_cli_backends.py +180 -0
- massgen/tests/test_code_execution.py +679 -0
- massgen/tests/test_external_agent_backend.py +134 -0
- massgen/tests/test_final_presentation_fallback.py +237 -0
- massgen/tests/test_gemini_planning_mode.py +351 -0
- massgen/tests/test_grok_backend.py +7 -10
- massgen/tests/test_http_mcp_server.py +42 -0
- massgen/tests/test_integration_simple.py +198 -0
- massgen/tests/test_mcp_blocking.py +125 -0
- massgen/tests/test_message_context_building.py +29 -47
- massgen/tests/test_orchestrator_final_presentation.py +48 -0
- massgen/tests/test_path_permission_manager.py +2087 -0
- massgen/tests/test_rich_terminal_display.py +14 -13
- massgen/tests/test_timeout.py +133 -0
- massgen/tests/test_v3_3agents.py +11 -12
- massgen/tests/test_v3_simple.py +8 -13
- massgen/tests/test_v3_three_agents.py +11 -18
- massgen/tests/test_v3_two_agents.py +8 -13
- massgen/token_manager/__init__.py +7 -0
- massgen/token_manager/token_manager.py +400 -0
- massgen/utils.py +52 -16
- massgen/v1/agent.py +45 -91
- massgen/v1/agents.py +18 -53
- massgen/v1/backends/gemini.py +50 -153
- massgen/v1/backends/grok.py +21 -54
- massgen/v1/backends/oai.py +39 -111
- massgen/v1/cli.py +36 -93
- massgen/v1/config.py +8 -12
- massgen/v1/logging.py +43 -127
- massgen/v1/main.py +18 -32
- massgen/v1/orchestrator.py +68 -209
- massgen/v1/streaming_display.py +62 -163
- massgen/v1/tools.py +8 -12
- massgen/v1/types.py +9 -23
- massgen/v1/utils.py +5 -23
- massgen-0.1.0.dist-info/METADATA +1245 -0
- massgen-0.1.0.dist-info/RECORD +273 -0
- massgen-0.1.0.dist-info/entry_points.txt +2 -0
- massgen/frontend/logging/__init__.py +0 -9
- massgen/frontend/logging/realtime_logger.py +0 -197
- massgen-0.0.3.dist-info/METADATA +0 -568
- massgen-0.0.3.dist-info/RECORD +0 -76
- massgen-0.0.3.dist-info/entry_points.txt +0 -2
- /massgen/backend/{Function calling openai responses.md → docs/Function calling openai responses.md} +0 -0
- {massgen-0.0.3.dist-info → massgen-0.1.0.dist-info}/WHEEL +0 -0
- {massgen-0.0.3.dist-info → massgen-0.1.0.dist-info}/licenses/LICENSE +0 -0
- {massgen-0.0.3.dist-info → massgen-0.1.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Response API formatter implementation.
|
|
4
|
+
Handles formatting for OpenAI Response API format with multimodal support.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any, Dict, List
|
|
10
|
+
|
|
11
|
+
from ._formatter_base import FormatterBase
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ResponseFormatter(FormatterBase):
|
|
15
|
+
"""Formatter for Response API format with multimodal support."""
|
|
16
|
+
|
|
17
|
+
def format_messages(self, messages: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
18
|
+
"""
|
|
19
|
+
Convert messages from Chat Completions format to Response API format.
|
|
20
|
+
|
|
21
|
+
Chat Completions tool message: {"role": "tool", "tool_call_id": "...", "content": "..."}
|
|
22
|
+
Response API tool message: {"type": "function_call_output", "call_id": "...", "output": "..."}
|
|
23
|
+
|
|
24
|
+
Also handles multimodal content conversion:
|
|
25
|
+
- {"type": "text", "text": "..."} → {"type": "input_text", "text": "..."}
|
|
26
|
+
- {"type": "image", "url": "..."} → {"type": "input_image", "image_url": "..."}
|
|
27
|
+
- {"type": "image", "base64": "..."} → {"type": "input_image", "image_url": "data:image/...;base64,..."}
|
|
28
|
+
|
|
29
|
+
Note: Assistant messages with tool_calls should not be in input - they're generated by the backend.
|
|
30
|
+
"""
|
|
31
|
+
cleaned_messages = []
|
|
32
|
+
for message in messages:
|
|
33
|
+
if "status" in message and "role" not in message:
|
|
34
|
+
# Create a copy without 'status'
|
|
35
|
+
cleaned_message = {k: v for k, v in message.items() if k != "status"}
|
|
36
|
+
cleaned_messages.append(cleaned_message)
|
|
37
|
+
else:
|
|
38
|
+
cleaned_messages.append(message)
|
|
39
|
+
|
|
40
|
+
converted_messages = []
|
|
41
|
+
|
|
42
|
+
for message in cleaned_messages:
|
|
43
|
+
if message.get("role") == "tool":
|
|
44
|
+
# Convert Chat Completions tool message to Response API format
|
|
45
|
+
converted_message = {
|
|
46
|
+
"type": "function_call_output",
|
|
47
|
+
"call_id": message.get("tool_call_id"),
|
|
48
|
+
"output": message.get("content", ""),
|
|
49
|
+
}
|
|
50
|
+
converted_messages.append(converted_message)
|
|
51
|
+
elif message.get("type") == "function_call_output":
|
|
52
|
+
# Already in Response API format
|
|
53
|
+
converted_messages.append(message)
|
|
54
|
+
elif message.get("role") == "assistant" and "tool_calls" in message:
|
|
55
|
+
# Assistant message with tool_calls - remove tool_calls when sending as input
|
|
56
|
+
cleaned_message = {k: v for k, v in message.items() if k != "tool_calls"}
|
|
57
|
+
converted_messages.append(cleaned_message)
|
|
58
|
+
else:
|
|
59
|
+
# For other message types, check for multimodal content
|
|
60
|
+
converted_message = self._convert_multimodal_content(message.copy())
|
|
61
|
+
converted_messages.append(converted_message)
|
|
62
|
+
|
|
63
|
+
return converted_messages
|
|
64
|
+
|
|
65
|
+
def _convert_multimodal_content(self, message: Dict[str, Any]) -> Dict[str, Any]:
|
|
66
|
+
"""
|
|
67
|
+
Convert multimodal content to Response API format.
|
|
68
|
+
|
|
69
|
+
Handles conversion of content arrays with text and image items:
|
|
70
|
+
- text items: {"type": "text", "text": "..."} → {"type": "input_text", "text": "..."}
|
|
71
|
+
- image URL: {"type": "image", "url": "..."} → {"type": "input_image", "image_url": "..."}
|
|
72
|
+
- image base64: {"type": "image", "base64": "...", "mime_type": "..."} →
|
|
73
|
+
{"type": "input_image", "image_url": "data:image/...;base64,..."}
|
|
74
|
+
- file: {"type": "file", "file_id": "..."} → {"type": "input_file", "file_id": "..."}
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
message: Message dictionary that may contain multimodal content
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Message with content converted to Response API format
|
|
81
|
+
"""
|
|
82
|
+
content = message.get("content")
|
|
83
|
+
|
|
84
|
+
# If content is not a list, no conversion needed
|
|
85
|
+
if not isinstance(content, list):
|
|
86
|
+
return message
|
|
87
|
+
|
|
88
|
+
converted_content = []
|
|
89
|
+
for item in content:
|
|
90
|
+
if not isinstance(item, dict):
|
|
91
|
+
# If item is a string, treat as text
|
|
92
|
+
converted_content.append({"type": "input_text", "text": str(item)})
|
|
93
|
+
continue
|
|
94
|
+
|
|
95
|
+
item_type = item.get("type")
|
|
96
|
+
|
|
97
|
+
if item_type == "text":
|
|
98
|
+
# Convert text item to input_text
|
|
99
|
+
converted_content.append(
|
|
100
|
+
{
|
|
101
|
+
"type": "input_text",
|
|
102
|
+
"text": item.get("text", ""),
|
|
103
|
+
},
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
elif item_type == "image":
|
|
107
|
+
# Convert image item to input_image
|
|
108
|
+
converted_item = self._convert_image_content(item)
|
|
109
|
+
if converted_item:
|
|
110
|
+
converted_content.append(converted_item)
|
|
111
|
+
|
|
112
|
+
elif item_type == "file":
|
|
113
|
+
# Convert file item to input_file
|
|
114
|
+
converted_content.append(
|
|
115
|
+
{
|
|
116
|
+
"type": "input_file",
|
|
117
|
+
"file_id": item.get("file_id", ""),
|
|
118
|
+
},
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
elif item_type == "file_pending_upload":
|
|
122
|
+
converted_content.append(item)
|
|
123
|
+
|
|
124
|
+
elif item_type in ["input_text", "input_image", "input_file"]:
|
|
125
|
+
# Already in Response API format
|
|
126
|
+
converted_content.append(item)
|
|
127
|
+
|
|
128
|
+
else:
|
|
129
|
+
# Unknown type, pass through
|
|
130
|
+
converted_content.append(item)
|
|
131
|
+
|
|
132
|
+
message["content"] = converted_content
|
|
133
|
+
return message
|
|
134
|
+
|
|
135
|
+
def _convert_image_content(self, image_item: Dict[str, Any]) -> Dict[str, Any]:
|
|
136
|
+
"""
|
|
137
|
+
Convert image content item to Response API format.
|
|
138
|
+
|
|
139
|
+
Supports:
|
|
140
|
+
- URL format: {"type": "image", "url": "https://..."}
|
|
141
|
+
- Base64 format: {"type": "image", "base64": "...", "mime_type": "image/jpeg"}
|
|
142
|
+
- Image URL format: {"type": "image", "image_url": "..."} (already correct)
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
image_item: Image content item dictionary
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
Converted image item in Response API format, or None if invalid
|
|
149
|
+
"""
|
|
150
|
+
# Check if already in correct format
|
|
151
|
+
if "image_url" in image_item:
|
|
152
|
+
return {
|
|
153
|
+
"type": "input_image",
|
|
154
|
+
"image_url": image_item["image_url"],
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
# Handle URL format
|
|
158
|
+
if "url" in image_item:
|
|
159
|
+
return {
|
|
160
|
+
"type": "input_image",
|
|
161
|
+
"image_url": image_item["url"],
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
# Handle base64 format
|
|
165
|
+
if "base64" in image_item:
|
|
166
|
+
mime_type = image_item.get("mime_type", "image/jpeg")
|
|
167
|
+
base64_data = image_item["base64"]
|
|
168
|
+
|
|
169
|
+
# Validate base64 data
|
|
170
|
+
if not self._validate_base64_image(base64_data):
|
|
171
|
+
return None
|
|
172
|
+
|
|
173
|
+
# Create data URL
|
|
174
|
+
data_url = f"data:{mime_type};base64,{base64_data}"
|
|
175
|
+
return {
|
|
176
|
+
"type": "input_image",
|
|
177
|
+
"image_url": data_url,
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
# No valid image data found
|
|
181
|
+
return None
|
|
182
|
+
|
|
183
|
+
def _validate_base64_image(self, base64_data: str) -> bool:
|
|
184
|
+
"""
|
|
185
|
+
Validate base64 image data.
|
|
186
|
+
|
|
187
|
+
Checks:
|
|
188
|
+
- Data is not empty
|
|
189
|
+
- Data is valid base64 (basic check)
|
|
190
|
+
- Data size is within limits (20MB)
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
base64_data: Base64 encoded image string
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
True if valid, False otherwise
|
|
197
|
+
"""
|
|
198
|
+
if not base64_data:
|
|
199
|
+
return False
|
|
200
|
+
|
|
201
|
+
# Check size (base64 is ~33% larger than binary)
|
|
202
|
+
# 20MB binary = ~27MB base64
|
|
203
|
+
max_base64_size = 27 * 1024 * 1024
|
|
204
|
+
if len(base64_data) > max_base64_size:
|
|
205
|
+
return False
|
|
206
|
+
|
|
207
|
+
# Basic base64 validation (contains only valid characters)
|
|
208
|
+
import re
|
|
209
|
+
|
|
210
|
+
if not re.match(r"^[A-Za-z0-9+/]*={0,2}$", base64_data):
|
|
211
|
+
return False
|
|
212
|
+
|
|
213
|
+
return True
|
|
214
|
+
|
|
215
|
+
def format_tools(self, tools: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
216
|
+
"""
|
|
217
|
+
Convert tools from Chat Completions format to Response API format if needed.
|
|
218
|
+
|
|
219
|
+
Chat Completions format: {"type": "function", "function": {"name": ..., "description": ..., "parameters": ...}}
|
|
220
|
+
Response API format: {"type": "function", "name": ..., "description": ..., "parameters": ...}
|
|
221
|
+
"""
|
|
222
|
+
if not tools:
|
|
223
|
+
return tools
|
|
224
|
+
|
|
225
|
+
converted_tools = []
|
|
226
|
+
for tool in tools:
|
|
227
|
+
if tool.get("type") == "function" and "function" in tool:
|
|
228
|
+
# Chat Completions format - convert to Response API format
|
|
229
|
+
func = tool["function"]
|
|
230
|
+
converted_tools.append(
|
|
231
|
+
{
|
|
232
|
+
"type": "function",
|
|
233
|
+
"name": func["name"],
|
|
234
|
+
"description": func["description"],
|
|
235
|
+
"parameters": func.get("parameters", {}),
|
|
236
|
+
},
|
|
237
|
+
)
|
|
238
|
+
else:
|
|
239
|
+
# Already in Response API format or non-function tool
|
|
240
|
+
converted_tools.append(tool)
|
|
241
|
+
|
|
242
|
+
return converted_tools
|
|
243
|
+
|
|
244
|
+
def format_mcp_tools(self, mcp_functions: Dict[str, Any]) -> List[Dict[str, Any]]:
|
|
245
|
+
"""Convert MCP tools to Response API format (OpenAI function declarations)."""
|
|
246
|
+
if not mcp_functions:
|
|
247
|
+
return []
|
|
248
|
+
|
|
249
|
+
converted_tools = []
|
|
250
|
+
for mcp_function in mcp_functions.values():
|
|
251
|
+
if hasattr(mcp_function, "to_openai_format"):
|
|
252
|
+
tool = mcp_function.to_openai_format()
|
|
253
|
+
else:
|
|
254
|
+
# Fallback format - Response API expects flat structure
|
|
255
|
+
tool = {
|
|
256
|
+
"type": "function",
|
|
257
|
+
"name": getattr(mcp_function, "name", "unknown"),
|
|
258
|
+
"description": getattr(mcp_function, "description", ""),
|
|
259
|
+
"parameters": getattr(mcp_function, "input_schema", {}),
|
|
260
|
+
}
|
|
261
|
+
converted_tools.append(tool)
|
|
262
|
+
|
|
263
|
+
return converted_tools
|
massgen/frontend/__init__.py
CHANGED
|
@@ -15,6 +15,5 @@ TODO - Missing Frontend Features from v0.0.1:
|
|
|
15
15
|
|
|
16
16
|
from .coordination_ui import CoordinationUI
|
|
17
17
|
from .displays import TerminalDisplay, SimpleDisplay
|
|
18
|
-
from .logging import RealtimeLogger
|
|
19
18
|
|
|
20
|
-
__all__ = ["CoordinationUI", "TerminalDisplay", "SimpleDisplay"
|
|
19
|
+
__all__ = ["CoordinationUI", "TerminalDisplay", "SimpleDisplay"]
|