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.

Files changed (268) hide show
  1. massgen/__init__.py +142 -8
  2. massgen/adapters/__init__.py +29 -0
  3. massgen/adapters/ag2_adapter.py +483 -0
  4. massgen/adapters/base.py +183 -0
  5. massgen/adapters/tests/__init__.py +0 -0
  6. massgen/adapters/tests/test_ag2_adapter.py +439 -0
  7. massgen/adapters/tests/test_agent_adapter.py +128 -0
  8. massgen/adapters/utils/__init__.py +2 -0
  9. massgen/adapters/utils/ag2_utils.py +236 -0
  10. massgen/adapters/utils/tests/__init__.py +0 -0
  11. massgen/adapters/utils/tests/test_ag2_utils.py +138 -0
  12. massgen/agent_config.py +329 -55
  13. massgen/api_params_handler/__init__.py +10 -0
  14. massgen/api_params_handler/_api_params_handler_base.py +99 -0
  15. massgen/api_params_handler/_chat_completions_api_params_handler.py +176 -0
  16. massgen/api_params_handler/_claude_api_params_handler.py +113 -0
  17. massgen/api_params_handler/_response_api_params_handler.py +130 -0
  18. massgen/backend/__init__.py +39 -4
  19. massgen/backend/azure_openai.py +385 -0
  20. massgen/backend/base.py +341 -69
  21. massgen/backend/base_with_mcp.py +1102 -0
  22. massgen/backend/capabilities.py +386 -0
  23. massgen/backend/chat_completions.py +577 -130
  24. massgen/backend/claude.py +1033 -537
  25. massgen/backend/claude_code.py +1203 -0
  26. massgen/backend/cli_base.py +209 -0
  27. massgen/backend/docs/BACKEND_ARCHITECTURE.md +126 -0
  28. massgen/backend/{CLAUDE_API_RESEARCH.md → docs/CLAUDE_API_RESEARCH.md} +18 -18
  29. massgen/backend/{GEMINI_API_DOCUMENTATION.md → docs/GEMINI_API_DOCUMENTATION.md} +9 -9
  30. massgen/backend/docs/Gemini MCP Integration Analysis.md +1050 -0
  31. massgen/backend/docs/MCP_IMPLEMENTATION_CLAUDE_BACKEND.md +177 -0
  32. massgen/backend/docs/MCP_INTEGRATION_RESPONSE_BACKEND.md +352 -0
  33. massgen/backend/docs/OPENAI_GPT5_MODELS.md +211 -0
  34. massgen/backend/{OPENAI_RESPONSES_API_FORMAT.md → docs/OPENAI_RESPONSE_API_TOOL_CALLS.md} +3 -3
  35. massgen/backend/docs/OPENAI_response_streaming.md +20654 -0
  36. massgen/backend/docs/inference_backend.md +257 -0
  37. massgen/backend/docs/permissions_and_context_files.md +1085 -0
  38. massgen/backend/external.py +126 -0
  39. massgen/backend/gemini.py +1850 -241
  40. massgen/backend/grok.py +40 -156
  41. massgen/backend/inference.py +156 -0
  42. massgen/backend/lmstudio.py +171 -0
  43. massgen/backend/response.py +1095 -322
  44. massgen/chat_agent.py +131 -113
  45. massgen/cli.py +1560 -275
  46. massgen/config_builder.py +2396 -0
  47. massgen/configs/BACKEND_CONFIGURATION.md +458 -0
  48. massgen/configs/README.md +559 -216
  49. massgen/configs/ag2/ag2_case_study.yaml +27 -0
  50. massgen/configs/ag2/ag2_coder.yaml +34 -0
  51. massgen/configs/ag2/ag2_coder_case_study.yaml +36 -0
  52. massgen/configs/ag2/ag2_gemini.yaml +27 -0
  53. massgen/configs/ag2/ag2_groupchat.yaml +108 -0
  54. massgen/configs/ag2/ag2_groupchat_gpt.yaml +118 -0
  55. massgen/configs/ag2/ag2_single_agent.yaml +21 -0
  56. massgen/configs/basic/multi/fast_timeout_example.yaml +37 -0
  57. massgen/configs/basic/multi/gemini_4o_claude.yaml +31 -0
  58. massgen/configs/basic/multi/gemini_gpt5nano_claude.yaml +36 -0
  59. massgen/configs/{gemini_4o_claude.yaml → basic/multi/geminicode_4o_claude.yaml} +3 -3
  60. massgen/configs/basic/multi/geminicode_gpt5nano_claude.yaml +36 -0
  61. massgen/configs/basic/multi/glm_gemini_claude.yaml +25 -0
  62. massgen/configs/basic/multi/gpt4o_audio_generation.yaml +30 -0
  63. massgen/configs/basic/multi/gpt4o_image_generation.yaml +31 -0
  64. massgen/configs/basic/multi/gpt5nano_glm_qwen.yaml +26 -0
  65. massgen/configs/basic/multi/gpt5nano_image_understanding.yaml +26 -0
  66. massgen/configs/{three_agents_default.yaml → basic/multi/three_agents_default.yaml} +8 -4
  67. massgen/configs/basic/multi/three_agents_opensource.yaml +27 -0
  68. massgen/configs/basic/multi/three_agents_vllm.yaml +20 -0
  69. massgen/configs/basic/multi/two_agents_gemini.yaml +19 -0
  70. massgen/configs/{two_agents.yaml → basic/multi/two_agents_gpt5.yaml} +14 -6
  71. massgen/configs/basic/multi/two_agents_opensource_lmstudio.yaml +31 -0
  72. massgen/configs/basic/multi/two_qwen_vllm_sglang.yaml +28 -0
  73. massgen/configs/{single_agent.yaml → basic/single/single_agent.yaml} +1 -1
  74. massgen/configs/{single_flash2.5.yaml → basic/single/single_flash2.5.yaml} +1 -2
  75. massgen/configs/basic/single/single_gemini2.5pro.yaml +16 -0
  76. massgen/configs/basic/single/single_gpt4o_audio_generation.yaml +22 -0
  77. massgen/configs/basic/single/single_gpt4o_image_generation.yaml +22 -0
  78. massgen/configs/basic/single/single_gpt4o_video_generation.yaml +24 -0
  79. massgen/configs/basic/single/single_gpt5nano.yaml +20 -0
  80. massgen/configs/basic/single/single_gpt5nano_file_search.yaml +18 -0
  81. massgen/configs/basic/single/single_gpt5nano_image_understanding.yaml +17 -0
  82. massgen/configs/basic/single/single_gptoss120b.yaml +15 -0
  83. massgen/configs/basic/single/single_openrouter_audio_understanding.yaml +15 -0
  84. massgen/configs/basic/single/single_qwen_video_understanding.yaml +15 -0
  85. massgen/configs/debug/code_execution/command_filtering_blacklist.yaml +29 -0
  86. massgen/configs/debug/code_execution/command_filtering_whitelist.yaml +28 -0
  87. massgen/configs/debug/code_execution/docker_verification.yaml +29 -0
  88. massgen/configs/debug/skip_coordination_test.yaml +27 -0
  89. massgen/configs/debug/test_sdk_migration.yaml +17 -0
  90. massgen/configs/docs/DISCORD_MCP_SETUP.md +208 -0
  91. massgen/configs/docs/TWITTER_MCP_ENESCINAR_SETUP.md +82 -0
  92. massgen/configs/providers/azure/azure_openai_multi.yaml +21 -0
  93. massgen/configs/providers/azure/azure_openai_single.yaml +19 -0
  94. massgen/configs/providers/claude/claude.yaml +14 -0
  95. massgen/configs/providers/gemini/gemini_gpt5nano.yaml +28 -0
  96. massgen/configs/providers/local/lmstudio.yaml +11 -0
  97. massgen/configs/providers/openai/gpt5.yaml +46 -0
  98. massgen/configs/providers/openai/gpt5_nano.yaml +46 -0
  99. massgen/configs/providers/others/grok_single_agent.yaml +19 -0
  100. massgen/configs/providers/others/zai_coding_team.yaml +108 -0
  101. massgen/configs/providers/others/zai_glm45.yaml +12 -0
  102. massgen/configs/{creative_team.yaml → teams/creative/creative_team.yaml} +16 -6
  103. massgen/configs/{travel_planning.yaml → teams/creative/travel_planning.yaml} +16 -6
  104. massgen/configs/{news_analysis.yaml → teams/research/news_analysis.yaml} +16 -6
  105. massgen/configs/{research_team.yaml → teams/research/research_team.yaml} +15 -7
  106. massgen/configs/{technical_analysis.yaml → teams/research/technical_analysis.yaml} +16 -6
  107. massgen/configs/tools/code-execution/basic_command_execution.yaml +25 -0
  108. massgen/configs/tools/code-execution/code_execution_use_case_simple.yaml +41 -0
  109. massgen/configs/tools/code-execution/docker_claude_code.yaml +32 -0
  110. massgen/configs/tools/code-execution/docker_multi_agent.yaml +32 -0
  111. massgen/configs/tools/code-execution/docker_simple.yaml +29 -0
  112. massgen/configs/tools/code-execution/docker_with_resource_limits.yaml +32 -0
  113. massgen/configs/tools/code-execution/multi_agent_playwright_automation.yaml +57 -0
  114. massgen/configs/tools/filesystem/cc_gpt5_gemini_filesystem.yaml +34 -0
  115. massgen/configs/tools/filesystem/claude_code_context_sharing.yaml +68 -0
  116. massgen/configs/tools/filesystem/claude_code_flash2.5.yaml +43 -0
  117. massgen/configs/tools/filesystem/claude_code_flash2.5_gptoss.yaml +49 -0
  118. massgen/configs/tools/filesystem/claude_code_gpt5nano.yaml +31 -0
  119. massgen/configs/tools/filesystem/claude_code_single.yaml +40 -0
  120. massgen/configs/tools/filesystem/fs_permissions_test.yaml +87 -0
  121. massgen/configs/tools/filesystem/gemini_gemini_workspace_cleanup.yaml +54 -0
  122. massgen/configs/tools/filesystem/gemini_gpt5_filesystem_casestudy.yaml +30 -0
  123. massgen/configs/tools/filesystem/gemini_gpt5nano_file_context_path.yaml +43 -0
  124. massgen/configs/tools/filesystem/gemini_gpt5nano_protected_paths.yaml +45 -0
  125. massgen/configs/tools/filesystem/gpt5mini_cc_fs_context_path.yaml +31 -0
  126. massgen/configs/tools/filesystem/grok4_gpt5_gemini_filesystem.yaml +32 -0
  127. massgen/configs/tools/filesystem/multiturn/grok4_gpt5_claude_code_filesystem_multiturn.yaml +58 -0
  128. massgen/configs/tools/filesystem/multiturn/grok4_gpt5_gemini_filesystem_multiturn.yaml +58 -0
  129. massgen/configs/tools/filesystem/multiturn/two_claude_code_filesystem_multiturn.yaml +47 -0
  130. massgen/configs/tools/filesystem/multiturn/two_gemini_flash_filesystem_multiturn.yaml +48 -0
  131. massgen/configs/tools/mcp/claude_code_discord_mcp_example.yaml +27 -0
  132. massgen/configs/tools/mcp/claude_code_simple_mcp.yaml +35 -0
  133. massgen/configs/tools/mcp/claude_code_twitter_mcp_example.yaml +32 -0
  134. massgen/configs/tools/mcp/claude_mcp_example.yaml +24 -0
  135. massgen/configs/tools/mcp/claude_mcp_test.yaml +27 -0
  136. massgen/configs/tools/mcp/five_agents_travel_mcp_test.yaml +157 -0
  137. massgen/configs/tools/mcp/five_agents_weather_mcp_test.yaml +103 -0
  138. massgen/configs/tools/mcp/gemini_mcp_example.yaml +24 -0
  139. massgen/configs/tools/mcp/gemini_mcp_filesystem_test.yaml +23 -0
  140. massgen/configs/tools/mcp/gemini_mcp_filesystem_test_sharing.yaml +23 -0
  141. massgen/configs/tools/mcp/gemini_mcp_filesystem_test_single_agent.yaml +17 -0
  142. massgen/configs/tools/mcp/gemini_mcp_filesystem_test_with_claude_code.yaml +24 -0
  143. massgen/configs/tools/mcp/gemini_mcp_test.yaml +27 -0
  144. massgen/configs/tools/mcp/gemini_notion_mcp.yaml +52 -0
  145. massgen/configs/tools/mcp/gpt5_nano_mcp_example.yaml +24 -0
  146. massgen/configs/tools/mcp/gpt5_nano_mcp_test.yaml +27 -0
  147. massgen/configs/tools/mcp/gpt5mini_claude_code_discord_mcp_example.yaml +38 -0
  148. massgen/configs/tools/mcp/gpt_oss_mcp_example.yaml +25 -0
  149. massgen/configs/tools/mcp/gpt_oss_mcp_test.yaml +28 -0
  150. massgen/configs/tools/mcp/grok3_mini_mcp_example.yaml +24 -0
  151. massgen/configs/tools/mcp/grok3_mini_mcp_test.yaml +27 -0
  152. massgen/configs/tools/mcp/multimcp_gemini.yaml +111 -0
  153. massgen/configs/tools/mcp/qwen_api_mcp_example.yaml +25 -0
  154. massgen/configs/tools/mcp/qwen_api_mcp_test.yaml +28 -0
  155. massgen/configs/tools/mcp/qwen_local_mcp_example.yaml +24 -0
  156. massgen/configs/tools/mcp/qwen_local_mcp_test.yaml +27 -0
  157. massgen/configs/tools/planning/five_agents_discord_mcp_planning_mode.yaml +140 -0
  158. massgen/configs/tools/planning/five_agents_filesystem_mcp_planning_mode.yaml +151 -0
  159. massgen/configs/tools/planning/five_agents_notion_mcp_planning_mode.yaml +151 -0
  160. massgen/configs/tools/planning/five_agents_twitter_mcp_planning_mode.yaml +155 -0
  161. massgen/configs/tools/planning/gpt5_mini_case_study_mcp_planning_mode.yaml +73 -0
  162. massgen/configs/tools/web-search/claude_streamable_http_test.yaml +43 -0
  163. massgen/configs/tools/web-search/gemini_streamable_http_test.yaml +43 -0
  164. massgen/configs/tools/web-search/gpt5_mini_streamable_http_test.yaml +43 -0
  165. massgen/configs/tools/web-search/gpt_oss_streamable_http_test.yaml +44 -0
  166. massgen/configs/tools/web-search/grok3_mini_streamable_http_test.yaml +43 -0
  167. massgen/configs/tools/web-search/qwen_api_streamable_http_test.yaml +44 -0
  168. massgen/configs/tools/web-search/qwen_local_streamable_http_test.yaml +43 -0
  169. massgen/coordination_tracker.py +708 -0
  170. massgen/docker/README.md +462 -0
  171. massgen/filesystem_manager/__init__.py +21 -0
  172. massgen/filesystem_manager/_base.py +9 -0
  173. massgen/filesystem_manager/_code_execution_server.py +545 -0
  174. massgen/filesystem_manager/_docker_manager.py +477 -0
  175. massgen/filesystem_manager/_file_operation_tracker.py +248 -0
  176. massgen/filesystem_manager/_filesystem_manager.py +813 -0
  177. massgen/filesystem_manager/_path_permission_manager.py +1261 -0
  178. massgen/filesystem_manager/_workspace_tools_server.py +1815 -0
  179. massgen/formatter/__init__.py +10 -0
  180. massgen/formatter/_chat_completions_formatter.py +284 -0
  181. massgen/formatter/_claude_formatter.py +235 -0
  182. massgen/formatter/_formatter_base.py +156 -0
  183. massgen/formatter/_response_formatter.py +263 -0
  184. massgen/frontend/__init__.py +1 -2
  185. massgen/frontend/coordination_ui.py +471 -286
  186. massgen/frontend/displays/base_display.py +56 -11
  187. massgen/frontend/displays/create_coordination_table.py +1956 -0
  188. massgen/frontend/displays/rich_terminal_display.py +1259 -619
  189. massgen/frontend/displays/simple_display.py +9 -4
  190. massgen/frontend/displays/terminal_display.py +27 -68
  191. massgen/logger_config.py +681 -0
  192. massgen/mcp_tools/README.md +232 -0
  193. massgen/mcp_tools/__init__.py +105 -0
  194. massgen/mcp_tools/backend_utils.py +1035 -0
  195. massgen/mcp_tools/circuit_breaker.py +195 -0
  196. massgen/mcp_tools/client.py +894 -0
  197. massgen/mcp_tools/config_validator.py +138 -0
  198. massgen/mcp_tools/docs/circuit_breaker.md +646 -0
  199. massgen/mcp_tools/docs/client.md +950 -0
  200. massgen/mcp_tools/docs/config_validator.md +478 -0
  201. massgen/mcp_tools/docs/exceptions.md +1165 -0
  202. massgen/mcp_tools/docs/security.md +854 -0
  203. massgen/mcp_tools/exceptions.py +338 -0
  204. massgen/mcp_tools/hooks.py +212 -0
  205. massgen/mcp_tools/security.py +780 -0
  206. massgen/message_templates.py +342 -64
  207. massgen/orchestrator.py +1515 -241
  208. massgen/stream_chunk/__init__.py +35 -0
  209. massgen/stream_chunk/base.py +92 -0
  210. massgen/stream_chunk/multimodal.py +237 -0
  211. massgen/stream_chunk/text.py +162 -0
  212. massgen/tests/mcp_test_server.py +150 -0
  213. massgen/tests/multi_turn_conversation_design.md +0 -8
  214. massgen/tests/test_azure_openai_backend.py +156 -0
  215. massgen/tests/test_backend_capabilities.py +262 -0
  216. massgen/tests/test_backend_event_loop_all.py +179 -0
  217. massgen/tests/test_chat_completions_refactor.py +142 -0
  218. massgen/tests/test_claude_backend.py +15 -28
  219. massgen/tests/test_claude_code.py +268 -0
  220. massgen/tests/test_claude_code_context_sharing.py +233 -0
  221. massgen/tests/test_claude_code_orchestrator.py +175 -0
  222. massgen/tests/test_cli_backends.py +180 -0
  223. massgen/tests/test_code_execution.py +679 -0
  224. massgen/tests/test_external_agent_backend.py +134 -0
  225. massgen/tests/test_final_presentation_fallback.py +237 -0
  226. massgen/tests/test_gemini_planning_mode.py +351 -0
  227. massgen/tests/test_grok_backend.py +7 -10
  228. massgen/tests/test_http_mcp_server.py +42 -0
  229. massgen/tests/test_integration_simple.py +198 -0
  230. massgen/tests/test_mcp_blocking.py +125 -0
  231. massgen/tests/test_message_context_building.py +29 -47
  232. massgen/tests/test_orchestrator_final_presentation.py +48 -0
  233. massgen/tests/test_path_permission_manager.py +2087 -0
  234. massgen/tests/test_rich_terminal_display.py +14 -13
  235. massgen/tests/test_timeout.py +133 -0
  236. massgen/tests/test_v3_3agents.py +11 -12
  237. massgen/tests/test_v3_simple.py +8 -13
  238. massgen/tests/test_v3_three_agents.py +11 -18
  239. massgen/tests/test_v3_two_agents.py +8 -13
  240. massgen/token_manager/__init__.py +7 -0
  241. massgen/token_manager/token_manager.py +400 -0
  242. massgen/utils.py +52 -16
  243. massgen/v1/agent.py +45 -91
  244. massgen/v1/agents.py +18 -53
  245. massgen/v1/backends/gemini.py +50 -153
  246. massgen/v1/backends/grok.py +21 -54
  247. massgen/v1/backends/oai.py +39 -111
  248. massgen/v1/cli.py +36 -93
  249. massgen/v1/config.py +8 -12
  250. massgen/v1/logging.py +43 -127
  251. massgen/v1/main.py +18 -32
  252. massgen/v1/orchestrator.py +68 -209
  253. massgen/v1/streaming_display.py +62 -163
  254. massgen/v1/tools.py +8 -12
  255. massgen/v1/types.py +9 -23
  256. massgen/v1/utils.py +5 -23
  257. massgen-0.1.0.dist-info/METADATA +1245 -0
  258. massgen-0.1.0.dist-info/RECORD +273 -0
  259. massgen-0.1.0.dist-info/entry_points.txt +2 -0
  260. massgen/frontend/logging/__init__.py +0 -9
  261. massgen/frontend/logging/realtime_logger.py +0 -197
  262. massgen-0.0.3.dist-info/METADATA +0 -568
  263. massgen-0.0.3.dist-info/RECORD +0 -76
  264. massgen-0.0.3.dist-info/entry_points.txt +0 -2
  265. /massgen/backend/{Function calling openai responses.md → docs/Function calling openai responses.md} +0 -0
  266. {massgen-0.0.3.dist-info → massgen-0.1.0.dist-info}/WHEEL +0 -0
  267. {massgen-0.0.3.dist-info → massgen-0.1.0.dist-info}/licenses/LICENSE +0 -0
  268. {massgen-0.0.3.dist-info → massgen-0.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,400 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Token and Cost Management Module
4
+ Provides unified token estimation and cost calculation for all backends.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import dataclass
10
+ from typing import Any, Dict, List, Optional, Union
11
+
12
+ from ..logger_config import logger
13
+
14
+
15
+ @dataclass
16
+ class TokenUsage:
17
+ """Token usage and cost tracking."""
18
+
19
+ input_tokens: int = 0
20
+ output_tokens: int = 0
21
+ estimated_cost: float = 0.0
22
+
23
+ def add(self, other: "TokenUsage"):
24
+ """Add another TokenUsage to this one."""
25
+ self.input_tokens += other.input_tokens
26
+ self.output_tokens += other.output_tokens
27
+ self.estimated_cost += other.estimated_cost
28
+
29
+ def reset(self):
30
+ """Reset all counters to zero."""
31
+ self.input_tokens = 0
32
+ self.output_tokens = 0
33
+ self.estimated_cost = 0.0
34
+
35
+
36
+ @dataclass
37
+ class ModelPricing:
38
+ """Pricing information for a model."""
39
+
40
+ input_cost_per_1k: float # Cost per 1000 input tokens
41
+ output_cost_per_1k: float # Cost per 1000 output tokens
42
+ context_window: Optional[int] = None
43
+ max_output_tokens: Optional[int] = None
44
+
45
+
46
+ class TokenCostCalculator:
47
+ """Unified token estimation and cost calculation."""
48
+
49
+ # Default pricing data for various providers and models
50
+ PROVIDER_PRICING: Dict[str, Dict[str, ModelPricing]] = {
51
+ "OpenAI": {
52
+ "gpt-4o": ModelPricing(0.0025, 0.01, 128000, 16384),
53
+ "gpt-4o-mini": ModelPricing(0.00015, 0.0006, 128000, 16384),
54
+ "gpt-4-turbo": ModelPricing(0.01, 0.03, 128000, 4096),
55
+ "gpt-4": ModelPricing(0.03, 0.06, 8192, 8192),
56
+ "gpt-3.5-turbo": ModelPricing(0.0005, 0.0015, 16385, 4096),
57
+ "o1-preview": ModelPricing(0.015, 0.06, 128000, 32768),
58
+ "o1-mini": ModelPricing(0.003, 0.012, 128000, 65536),
59
+ "o3-mini": ModelPricing(0.0011, 0.0044, 200000, 100000),
60
+ },
61
+ "Anthropic": {
62
+ "claude-3-5-sonnet": ModelPricing(0.003, 0.015, 200000, 8192),
63
+ "claude-3-5-haiku": ModelPricing(0.001, 0.005, 200000, 8192),
64
+ "claude-3-opus": ModelPricing(0.015, 0.075, 200000, 4096),
65
+ "claude-3-sonnet": ModelPricing(0.003, 0.015, 200000, 4096),
66
+ "claude-3-haiku": ModelPricing(0.00025, 0.00125, 200000, 4096),
67
+ },
68
+ "Google": {
69
+ "gemini-2.0-flash-exp": ModelPricing(0.0, 0.0, 1048576, 8192), # Free during experimental
70
+ "gemini-2.0-flash-thinking-exp": ModelPricing(0.0, 0.0, 32767, 8192),
71
+ "gemini-1.5-pro": ModelPricing(0.00125, 0.005, 2097152, 8192),
72
+ "gemini-1.5-flash": ModelPricing(0.000075, 0.0003, 1048576, 8192),
73
+ "gemini-1.5-flash-8b": ModelPricing(0.0000375, 0.00015, 1048576, 8192),
74
+ "gemini-1.0-pro": ModelPricing(0.00025, 0.00125, 32760, 8192),
75
+ },
76
+ "Cerebras": {
77
+ "llama3.3-70b": ModelPricing(0.00035, 0.00035, 128000, 8192),
78
+ "llama3.1-70b": ModelPricing(0.00035, 0.00035, 128000, 8192),
79
+ "llama3.1-8b": ModelPricing(0.00001, 0.00001, 128000, 8192),
80
+ },
81
+ "Together": {
82
+ "meta-llama/Llama-3.3-70B-Instruct-Turbo": ModelPricing(0.00059, 0.00079, 128000, 32768),
83
+ "meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo": ModelPricing(0.00059, 0.00079, 128000, 32768),
84
+ "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo": ModelPricing(0.00088, 0.00088, 130000, 4096),
85
+ "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo": ModelPricing(0.00018, 0.00018, 131072, 65536),
86
+ "meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo": ModelPricing(0.00006, 0.00006, 131072, 16384),
87
+ "Qwen/QwQ-32B-Preview": ModelPricing(0.00015, 0.00015, 32768, 32768),
88
+ "Qwen/Qwen2.5-72B-Instruct-Turbo": ModelPricing(0.00012, 0.00012, 32768, 8192),
89
+ "mistralai/Mixtral-8x22B-Instruct-v0.1": ModelPricing(0.0009, 0.0009, 65536, 65536),
90
+ "deepseek-ai/deepseek-r1-distill-llama-70b": ModelPricing(0.00015, 0.00015, 65536, 8192),
91
+ },
92
+ "Fireworks": {
93
+ "llama-3.3-70b": ModelPricing(0.0002, 0.0002, 128000, 16384),
94
+ "llama-3.1-405b": ModelPricing(0.0009, 0.0009, 131072, 16384),
95
+ "llama-3.1-70b": ModelPricing(0.0002, 0.0002, 131072, 16384),
96
+ "llama-3.1-8b": ModelPricing(0.00002, 0.00002, 131072, 16384),
97
+ "qwen2.5-72b": ModelPricing(0.0002, 0.0002, 32768, 16384),
98
+ },
99
+ "Groq": {
100
+ "llama-3.3-70b-versatile": ModelPricing(0.00059, 0.00079, 128000, 32768),
101
+ "llama-3.1-70b-versatile": ModelPricing(0.00059, 0.00079, 131072, 8000),
102
+ "llama-3.1-8b-instant": ModelPricing(0.00005, 0.00008, 131072, 8000),
103
+ "mixtral-8x7b-32768": ModelPricing(0.00024, 0.00024, 32768, 32768),
104
+ },
105
+ "xAI": {
106
+ "grok-2-latest": ModelPricing(0.005, 0.015, 131072, 131072),
107
+ "grok-2": ModelPricing(0.005, 0.015, 131072, 131072),
108
+ "grok-2-mini": ModelPricing(0.001, 0.003, 131072, 65536),
109
+ },
110
+ "DeepSeek": {
111
+ "deepseek-reasoner": ModelPricing(0.00014, 0.0028, 163840, 8192),
112
+ "deepseek-chat": ModelPricing(0.00014, 0.00028, 64000, 8192),
113
+ },
114
+ }
115
+
116
+ def __init__(self):
117
+ """Initialize the calculator with optional tiktoken for accurate estimation."""
118
+ self.tiktoken_encoder = None
119
+ self._try_init_tiktoken()
120
+
121
+ def _try_init_tiktoken(self):
122
+ """Try to initialize tiktoken encoder for more accurate token counting."""
123
+ try:
124
+ import tiktoken
125
+
126
+ # Use cl100k_base encoder (GPT-4/GPT-3.5-turbo tokenizer)
127
+ self.tiktoken_encoder = tiktoken.get_encoding("cl100k_base")
128
+ logger.debug("Tiktoken encoder initialized for accurate token counting")
129
+ except ImportError:
130
+ logger.debug("Tiktoken not available, using simple estimation")
131
+ except Exception as e:
132
+ logger.warning(f"Failed to initialize tiktoken: {e}")
133
+
134
+ def estimate_tokens(self, text: Union[str, List[Dict[str, Any]]], method: str = "auto") -> int:
135
+ """
136
+ Estimate token count for text or messages.
137
+
138
+ Args:
139
+ text: Text string or list of message dictionaries
140
+ method: Estimation method ("tiktoken", "simple", "auto")
141
+
142
+ Returns:
143
+ Estimated token count
144
+ """
145
+ # Convert messages to text if needed
146
+ if isinstance(text, list):
147
+ text = self._messages_to_text(text)
148
+
149
+ if method == "auto":
150
+ # Use tiktoken if available, otherwise simple
151
+ if self.tiktoken_encoder:
152
+ return self.estimate_tokens_tiktoken(text)
153
+ else:
154
+ return self.estimate_tokens_simple(text)
155
+ elif method == "tiktoken":
156
+ return self.estimate_tokens_tiktoken(text)
157
+ else:
158
+ return self.estimate_tokens_simple(text)
159
+
160
+ def estimate_tokens_tiktoken(self, text: str) -> int:
161
+ """
162
+ Estimate tokens using tiktoken (OpenAI's tokenizer).
163
+ Most accurate for OpenAI models.
164
+
165
+ Args:
166
+ text: Text to estimate
167
+
168
+ Returns:
169
+ Token count
170
+ """
171
+ if not self.tiktoken_encoder:
172
+ logger.warning("Tiktoken not available, falling back to simple estimation")
173
+ return self.estimate_tokens_simple(text)
174
+
175
+ try:
176
+ tokens = self.tiktoken_encoder.encode(text)
177
+ return len(tokens)
178
+ except Exception as e:
179
+ logger.warning(f"Tiktoken encoding failed: {e}, using simple estimation")
180
+ return self.estimate_tokens_simple(text)
181
+
182
+ def estimate_tokens_simple(self, text: str) -> int:
183
+ """
184
+ Simple token estimation based on character/word count.
185
+ Roughly 1 token ≈ 4 characters or 0.75 words.
186
+
187
+ Args:
188
+ text: Text to estimate
189
+
190
+ Returns:
191
+ Estimated token count
192
+ """
193
+ # Method 1: Character-based (1 token ≈ 4 characters)
194
+ char_estimate = len(text) / 4
195
+
196
+ # Method 2: Word-based (1 token ≈ 0.75 words)
197
+ words = text.split()
198
+ word_estimate = len(words) / 0.75
199
+
200
+ # Take average of both methods for better accuracy
201
+ estimate = (char_estimate + word_estimate) / 2
202
+
203
+ return int(estimate)
204
+
205
+ def _messages_to_text(self, messages: List[Dict[str, Any]]) -> str:
206
+ """Convert message list to text for token estimation."""
207
+ text_parts = []
208
+
209
+ for msg in messages:
210
+ role = msg.get("role", "")
211
+ content = msg.get("content", "")
212
+
213
+ # Handle different content types
214
+ if isinstance(content, str):
215
+ text_parts.append(f"{role}: {content}")
216
+ elif isinstance(content, list):
217
+ # Handle structured content (like Claude's format)
218
+ for item in content:
219
+ if isinstance(item, dict):
220
+ if item.get("type") == "text":
221
+ text_parts.append(f"{role}: {item.get('text', '')}")
222
+ elif item.get("type") == "tool_result":
223
+ text_parts.append(f"tool_result: {item.get('content', '')}")
224
+ else:
225
+ text_parts.append(f"{role}: {str(item)}")
226
+ else:
227
+ text_parts.append(f"{role}: {str(content)}")
228
+
229
+ # Add tool calls if present
230
+ if "tool_calls" in msg:
231
+ tool_calls = msg["tool_calls"]
232
+ if isinstance(tool_calls, list):
233
+ for call in tool_calls:
234
+ text_parts.append(f"tool_call: {str(call)}")
235
+
236
+ return "\n".join(text_parts)
237
+
238
+ def get_model_pricing(self, provider: str, model: str) -> Optional[ModelPricing]:
239
+ """
240
+ Get pricing information for a specific model.
241
+
242
+ Args:
243
+ provider: Provider name (e.g., "OpenAI", "Anthropic")
244
+ model: Model name or identifier
245
+
246
+ Returns:
247
+ ModelPricing object or None if not found
248
+ """
249
+ # Normalize provider name
250
+ provider = self._normalize_provider(provider)
251
+
252
+ # Get provider pricing data
253
+ provider_models = self.PROVIDER_PRICING.get(provider, {})
254
+
255
+ # Try exact match first
256
+ if model in provider_models:
257
+ return provider_models[model]
258
+
259
+ # Try to find by partial match
260
+ for model_key, pricing in provider_models.items():
261
+ if model_key.lower() in model.lower() or model.lower() in model_key.lower():
262
+ return pricing
263
+
264
+ # Try to infer from model name patterns
265
+ model_lower = model.lower()
266
+
267
+ # GPT-4 variants
268
+ if "gpt-4o" in model_lower and "mini" in model_lower:
269
+ return provider_models.get("gpt-4o-mini")
270
+ elif "gpt-4o" in model_lower:
271
+ return provider_models.get("gpt-4o")
272
+ elif "gpt-4" in model_lower and "turbo" in model_lower:
273
+ return provider_models.get("gpt-4-turbo")
274
+ elif "gpt-4" in model_lower:
275
+ return provider_models.get("gpt-4")
276
+ elif "gpt-3.5" in model_lower:
277
+ return provider_models.get("gpt-3.5-turbo")
278
+
279
+ # Claude variants
280
+ elif "claude-3-5-sonnet" in model_lower or "claude-3.5-sonnet" in model_lower:
281
+ return provider_models.get("claude-3-5-sonnet")
282
+ elif "claude-3-5-haiku" in model_lower or "claude-3.5-haiku" in model_lower:
283
+ return provider_models.get("claude-3-5-haiku")
284
+ elif "claude-3-opus" in model_lower:
285
+ return provider_models.get("claude-3-opus")
286
+ elif "claude-3-sonnet" in model_lower:
287
+ return provider_models.get("claude-3-sonnet")
288
+ elif "claude-3-haiku" in model_lower:
289
+ return provider_models.get("claude-3-haiku")
290
+
291
+ # Gemini variants
292
+ elif "gemini-2" in model_lower and "flash" in model_lower:
293
+ return provider_models.get("gemini-2.0-flash-exp")
294
+ elif "gemini-1.5-pro" in model_lower:
295
+ return provider_models.get("gemini-1.5-pro")
296
+ elif "gemini-1.5-flash" in model_lower:
297
+ return provider_models.get("gemini-1.5-flash")
298
+
299
+ logger.debug(f"No pricing found for {provider}/{model}")
300
+ return None
301
+
302
+ def _normalize_provider(self, provider: str) -> str:
303
+ """Normalize provider name for lookup."""
304
+ provider_map = {
305
+ "openai": "OpenAI",
306
+ "anthropic": "Anthropic",
307
+ "claude": "Anthropic",
308
+ "google": "Google",
309
+ "gemini": "Google",
310
+ "vertex": "Google",
311
+ "cerebras": "Cerebras",
312
+ "cerebras ai": "Cerebras",
313
+ "together": "Together",
314
+ "together ai": "Together",
315
+ "fireworks": "Fireworks",
316
+ "fireworks ai": "Fireworks",
317
+ "groq": "Groq",
318
+ "xai": "xAI",
319
+ "x.ai": "xAI",
320
+ "grok": "xAI",
321
+ "deepseek": "DeepSeek",
322
+ }
323
+
324
+ provider_lower = provider.lower()
325
+ return provider_map.get(provider_lower, provider)
326
+
327
+ def calculate_cost(self, input_tokens: int, output_tokens: int, provider: str, model: str) -> float:
328
+ """
329
+ Calculate cost for token usage.
330
+
331
+ Args:
332
+ input_tokens: Number of input tokens
333
+ output_tokens: Number of output tokens
334
+ provider: Provider name
335
+ model: Model name
336
+
337
+ Returns:
338
+ Estimated cost in USD
339
+ """
340
+ pricing = self.get_model_pricing(provider, model)
341
+
342
+ if not pricing:
343
+ logger.debug(f"No pricing for {provider}/{model}, returning 0")
344
+ return 0.0
345
+
346
+ # Calculate costs (prices are per 1000 tokens)
347
+ input_cost = (input_tokens / 1000) * pricing.input_cost_per_1k
348
+ output_cost = (output_tokens / 1000) * pricing.output_cost_per_1k
349
+
350
+ total_cost = input_cost + output_cost
351
+
352
+ logger.debug(
353
+ f"Cost calculation for {provider}/{model}: "
354
+ f"{input_tokens} input @ ${pricing.input_cost_per_1k}/1k = ${input_cost:.4f}, "
355
+ f"{output_tokens} output @ ${pricing.output_cost_per_1k}/1k = ${output_cost:.4f}, "
356
+ f"total = ${total_cost:.4f}",
357
+ )
358
+
359
+ return total_cost
360
+
361
+ def update_token_usage(self, usage: TokenUsage, messages: List[Dict[str, Any]], response_content: str, provider: str, model: str) -> TokenUsage:
362
+ """
363
+ Update token usage with new conversation turn.
364
+
365
+ Args:
366
+ usage: Existing TokenUsage to update
367
+ messages: Input messages
368
+ response_content: Response content
369
+ provider: Provider name
370
+ model: Model name
371
+
372
+ Returns:
373
+ Updated TokenUsage object
374
+ """
375
+ # Estimate tokens
376
+ input_tokens = self.estimate_tokens(messages)
377
+ output_tokens = self.estimate_tokens(response_content)
378
+
379
+ # Calculate cost
380
+ cost = self.calculate_cost(input_tokens, output_tokens, provider, model)
381
+
382
+ # Update usage
383
+ usage.input_tokens += input_tokens
384
+ usage.output_tokens += output_tokens
385
+ usage.estimated_cost += cost
386
+
387
+ return usage
388
+
389
+ def format_cost(self, cost: float) -> str:
390
+ """Format cost for display."""
391
+ if cost < 0.01:
392
+ return f"${cost:.4f}"
393
+ elif cost < 1.0:
394
+ return f"${cost:.3f}"
395
+ else:
396
+ return f"${cost:.2f}"
397
+
398
+ def format_usage_summary(self, usage: TokenUsage) -> str:
399
+ """Format token usage summary for display."""
400
+ return f"Tokens: {usage.input_tokens:,} input, " f"{usage.output_tokens:,} output, " f"Cost: {self.format_cost(usage.estimated_cost)}"
massgen/utils.py CHANGED
@@ -1,17 +1,52 @@
1
- from dataclasses import dataclass
2
- from datetime import datetime
3
- from typing import Any, Union, Optional, Dict, List
1
+ # -*- coding: utf-8 -*-
2
+ from enum import Enum
3
+
4
+
5
+ class ActionType(Enum):
6
+ """All types of actions an agent can take -- TODO: Note this is also in masgen/backend/gemini.py; we should have an enums or utils file with this."""
7
+
8
+ NEW_ANSWER = "answer"
9
+ VOTE = "vote"
10
+ VOTE_IGNORED = "vote_ignored" # Vote was cast but ignored due to restart
11
+ ERROR = "error"
12
+ TIMEOUT = "timeout"
13
+ CANCELLED = "cancelled"
14
+
15
+
16
+ class AgentStatus(Enum):
17
+ """All types of states an agent can be in during coordination."""
18
+
19
+ STREAMING = "streaming" # actively streaming content/reasoning
20
+ VOTED = "voted" # has cast their vote for this round
21
+ ANSWERED = "answered" # has provided an answer this round
22
+ RESTARTING = "restarting" # restarting due to new answer from another agent
23
+ ERROR = "error" # encountered an error
24
+ TIMEOUT = "timeout" # timed out
25
+ COMPLETED = "completed" # finished all work -- will not be called again
26
+
27
+
28
+ class CoordinationStage(Enum):
29
+ """Stages of the coordination process."""
30
+
31
+ INITIAL_ANSWER = "initial_answer" # initial answer generation
32
+ ENFORCEMENT = "enforcement"
33
+ PRESENTATION = "presentation"
34
+
4
35
 
5
36
  MODEL_MAPPINGS = {
6
37
  "openai": [
38
+ # GPT-5 variants
39
+ "gpt-5",
40
+ "gpt-5-mini",
41
+ "gpt-5-nano",
42
+ "gpt-5-codex",
7
43
  # GPT-4.1 variants
8
44
  "gpt-4.1",
9
45
  "gpt-4.1-mini",
46
+ "gpt-4.1-nano",
10
47
  # GPT-4o variants
11
48
  "gpt-4o-mini",
12
49
  "gpt-4o",
13
- # o1
14
- "o1", # -> o1-2024-12-17
15
50
  # o3
16
51
  "o3",
17
52
  "o3-low",
@@ -29,22 +64,19 @@ MODEL_MAPPINGS = {
29
64
  "o4-mini-high",
30
65
  ],
31
66
  "claude": [
67
+ # Claude 4.5 variants
68
+ "claude-sonnet-4-5-20250929",
69
+ # Claude 4 variants
70
+ "claude-opus-4-1-20250805",
71
+ "claude-opus-4-20250514",
72
+ "claude-sonnet-4-20250514",
32
73
  # Claude 3.5 variants
33
74
  "claude-3-5-sonnet-latest",
34
75
  "claude-3-5-haiku-latest",
35
- "claude-3-5-sonnet-20241022",
76
+ "claude-3-5-sonnet-20250114",
36
77
  "claude-3-5-haiku-20241022",
37
78
  # Claude 3 variants
38
- "claude-3-sonnet-20240229",
39
79
  "claude-3-opus-20240229",
40
- "claude-3-haiku-20240307",
41
- # Claude 2 variants
42
- "claude-2.1",
43
- "claude-2.0",
44
- # Claude instant
45
- "claude-instant-1.2",
46
- # Sonnet 4
47
- "claude-sonnet-4-20250514",
48
80
  ],
49
81
  "gemini": [
50
82
  "gemini-2.5-flash",
@@ -55,6 +87,10 @@ MODEL_MAPPINGS = {
55
87
  "grok-3",
56
88
  "grok-4",
57
89
  ],
90
+ "zai": [
91
+ "glm-4.5",
92
+ "glm-4.5-air",
93
+ ],
58
94
  }
59
95
 
60
96
 
@@ -66,7 +102,7 @@ def get_backend_type_from_model(model: str) -> str:
66
102
  model: The model name (e.g., "gpt-4", "gemini-pro", "grok-1")
67
103
 
68
104
  Returns:
69
- Agent type string ("openai", "gemini", "grok")
105
+ Agent type string ("openai", "gemini", "grok", etc.)
70
106
  """
71
107
  if not model:
72
108
  return "openai" # Default to OpenAI