llms-py 3.0.0__py3-none-any.whl → 3.0.0b1__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.
Files changed (206) hide show
  1. llms/index.html +77 -35
  2. llms/llms.json +23 -72
  3. llms/main.py +732 -1786
  4. llms/providers.json +1 -1
  5. llms/{extensions/analytics/ui/index.mjs → ui/Analytics.mjs} +238 -154
  6. llms/ui/App.mjs +60 -151
  7. llms/ui/Avatar.mjs +85 -0
  8. llms/ui/Brand.mjs +52 -0
  9. llms/ui/ChatPrompt.mjs +606 -0
  10. llms/ui/Main.mjs +873 -0
  11. llms/ui/ModelSelector.mjs +693 -0
  12. llms/ui/OAuthSignIn.mjs +92 -0
  13. llms/ui/ProviderIcon.mjs +36 -0
  14. llms/ui/ProviderStatus.mjs +105 -0
  15. llms/{extensions/app/ui → ui}/Recents.mjs +65 -91
  16. llms/ui/{modules/chat/SettingsDialog.mjs → SettingsDialog.mjs} +9 -9
  17. llms/{extensions/app/ui/index.mjs → ui/Sidebar.mjs} +58 -124
  18. llms/ui/SignIn.mjs +64 -0
  19. llms/ui/SystemPromptEditor.mjs +31 -0
  20. llms/ui/SystemPromptSelector.mjs +56 -0
  21. llms/ui/Welcome.mjs +8 -0
  22. llms/ui/ai.mjs +53 -125
  23. llms/ui/app.css +111 -1837
  24. llms/ui/lib/charts.mjs +13 -9
  25. llms/ui/lib/servicestack-vue.mjs +3 -3
  26. llms/ui/lib/vue.min.mjs +9 -10
  27. llms/ui/lib/vue.mjs +1602 -1763
  28. llms/ui/markdown.mjs +2 -10
  29. llms/ui/tailwind.input.css +80 -496
  30. llms/ui/threadStore.mjs +572 -0
  31. llms/ui/utils.mjs +117 -113
  32. llms/ui.json +1069 -0
  33. {llms_py-3.0.0.dist-info → llms_py-3.0.0b1.dist-info}/METADATA +1 -1
  34. llms_py-3.0.0b1.dist-info/RECORD +49 -0
  35. llms/__pycache__/__init__.cpython-312.pyc +0 -0
  36. llms/__pycache__/__init__.cpython-313.pyc +0 -0
  37. llms/__pycache__/__init__.cpython-314.pyc +0 -0
  38. llms/__pycache__/__main__.cpython-312.pyc +0 -0
  39. llms/__pycache__/__main__.cpython-314.pyc +0 -0
  40. llms/__pycache__/llms.cpython-312.pyc +0 -0
  41. llms/__pycache__/main.cpython-312.pyc +0 -0
  42. llms/__pycache__/main.cpython-313.pyc +0 -0
  43. llms/__pycache__/main.cpython-314.pyc +0 -0
  44. llms/__pycache__/plugins.cpython-314.pyc +0 -0
  45. llms/extensions/app/README.md +0 -20
  46. llms/extensions/app/__init__.py +0 -530
  47. llms/extensions/app/__pycache__/__init__.cpython-314.pyc +0 -0
  48. llms/extensions/app/__pycache__/db.cpython-314.pyc +0 -0
  49. llms/extensions/app/__pycache__/db_manager.cpython-314.pyc +0 -0
  50. llms/extensions/app/db.py +0 -644
  51. llms/extensions/app/db_manager.py +0 -195
  52. llms/extensions/app/requests.json +0 -9073
  53. llms/extensions/app/threads.json +0 -15290
  54. llms/extensions/app/ui/threadStore.mjs +0 -411
  55. llms/extensions/core_tools/CALCULATOR.md +0 -32
  56. llms/extensions/core_tools/__init__.py +0 -598
  57. llms/extensions/core_tools/__pycache__/__init__.cpython-314.pyc +0 -0
  58. llms/extensions/core_tools/ui/codemirror/addon/edit/closebrackets.js +0 -201
  59. llms/extensions/core_tools/ui/codemirror/addon/edit/closetag.js +0 -185
  60. llms/extensions/core_tools/ui/codemirror/addon/edit/continuelist.js +0 -101
  61. llms/extensions/core_tools/ui/codemirror/addon/edit/matchbrackets.js +0 -160
  62. llms/extensions/core_tools/ui/codemirror/addon/edit/matchtags.js +0 -66
  63. llms/extensions/core_tools/ui/codemirror/addon/edit/trailingspace.js +0 -27
  64. llms/extensions/core_tools/ui/codemirror/addon/selection/active-line.js +0 -72
  65. llms/extensions/core_tools/ui/codemirror/addon/selection/mark-selection.js +0 -119
  66. llms/extensions/core_tools/ui/codemirror/addon/selection/selection-pointer.js +0 -98
  67. llms/extensions/core_tools/ui/codemirror/doc/docs.css +0 -225
  68. llms/extensions/core_tools/ui/codemirror/doc/source_sans.woff +0 -0
  69. llms/extensions/core_tools/ui/codemirror/lib/codemirror.css +0 -344
  70. llms/extensions/core_tools/ui/codemirror/lib/codemirror.js +0 -9884
  71. llms/extensions/core_tools/ui/codemirror/mode/clike/clike.js +0 -942
  72. llms/extensions/core_tools/ui/codemirror/mode/javascript/index.html +0 -118
  73. llms/extensions/core_tools/ui/codemirror/mode/javascript/javascript.js +0 -962
  74. llms/extensions/core_tools/ui/codemirror/mode/javascript/typescript.html +0 -62
  75. llms/extensions/core_tools/ui/codemirror/mode/python/python.js +0 -402
  76. llms/extensions/core_tools/ui/codemirror/theme/dracula.css +0 -40
  77. llms/extensions/core_tools/ui/codemirror/theme/mocha.css +0 -135
  78. llms/extensions/core_tools/ui/index.mjs +0 -650
  79. llms/extensions/gallery/README.md +0 -61
  80. llms/extensions/gallery/__init__.py +0 -61
  81. llms/extensions/gallery/__pycache__/__init__.cpython-314.pyc +0 -0
  82. llms/extensions/gallery/__pycache__/db.cpython-314.pyc +0 -0
  83. llms/extensions/gallery/db.py +0 -298
  84. llms/extensions/gallery/ui/index.mjs +0 -482
  85. llms/extensions/katex/README.md +0 -39
  86. llms/extensions/katex/__init__.py +0 -6
  87. llms/extensions/katex/__pycache__/__init__.cpython-314.pyc +0 -0
  88. llms/extensions/katex/ui/README.md +0 -125
  89. llms/extensions/katex/ui/contrib/auto-render.js +0 -338
  90. llms/extensions/katex/ui/contrib/auto-render.min.js +0 -1
  91. llms/extensions/katex/ui/contrib/auto-render.mjs +0 -244
  92. llms/extensions/katex/ui/contrib/copy-tex.js +0 -127
  93. llms/extensions/katex/ui/contrib/copy-tex.min.js +0 -1
  94. llms/extensions/katex/ui/contrib/copy-tex.mjs +0 -105
  95. llms/extensions/katex/ui/contrib/mathtex-script-type.js +0 -109
  96. llms/extensions/katex/ui/contrib/mathtex-script-type.min.js +0 -1
  97. llms/extensions/katex/ui/contrib/mathtex-script-type.mjs +0 -24
  98. llms/extensions/katex/ui/contrib/mhchem.js +0 -3213
  99. llms/extensions/katex/ui/contrib/mhchem.min.js +0 -1
  100. llms/extensions/katex/ui/contrib/mhchem.mjs +0 -3109
  101. llms/extensions/katex/ui/contrib/render-a11y-string.js +0 -887
  102. llms/extensions/katex/ui/contrib/render-a11y-string.min.js +0 -1
  103. llms/extensions/katex/ui/contrib/render-a11y-string.mjs +0 -800
  104. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.ttf +0 -0
  105. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff +0 -0
  106. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  107. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  108. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  109. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  110. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  111. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  112. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  113. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  114. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  115. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  116. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  117. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  118. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  119. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.ttf +0 -0
  120. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff +0 -0
  121. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff2 +0 -0
  122. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  123. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  124. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  125. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.ttf +0 -0
  126. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff +0 -0
  127. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff2 +0 -0
  128. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.ttf +0 -0
  129. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff +0 -0
  130. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff2 +0 -0
  131. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  132. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  133. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  134. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.ttf +0 -0
  135. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff +0 -0
  136. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff2 +0 -0
  137. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  138. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  139. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  140. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  141. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  142. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  143. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  144. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  145. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  146. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.ttf +0 -0
  147. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff +0 -0
  148. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff2 +0 -0
  149. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.ttf +0 -0
  150. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff +0 -0
  151. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  152. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.ttf +0 -0
  153. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff +0 -0
  154. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  155. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.ttf +0 -0
  156. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff +0 -0
  157. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  158. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.ttf +0 -0
  159. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff +0 -0
  160. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  161. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  162. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  163. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  164. llms/extensions/katex/ui/index.mjs +0 -92
  165. llms/extensions/katex/ui/katex-swap.css +0 -1230
  166. llms/extensions/katex/ui/katex-swap.min.css +0 -1
  167. llms/extensions/katex/ui/katex.css +0 -1230
  168. llms/extensions/katex/ui/katex.js +0 -19080
  169. llms/extensions/katex/ui/katex.min.css +0 -1
  170. llms/extensions/katex/ui/katex.min.js +0 -1
  171. llms/extensions/katex/ui/katex.min.mjs +0 -1
  172. llms/extensions/katex/ui/katex.mjs +0 -18547
  173. llms/extensions/providers/__init__.py +0 -18
  174. llms/extensions/providers/__pycache__/__init__.cpython-314.pyc +0 -0
  175. llms/extensions/providers/__pycache__/anthropic.cpython-314.pyc +0 -0
  176. llms/extensions/providers/__pycache__/chutes.cpython-314.pyc +0 -0
  177. llms/extensions/providers/__pycache__/google.cpython-314.pyc +0 -0
  178. llms/extensions/providers/__pycache__/nvidia.cpython-314.pyc +0 -0
  179. llms/extensions/providers/__pycache__/openai.cpython-314.pyc +0 -0
  180. llms/extensions/providers/__pycache__/openrouter.cpython-314.pyc +0 -0
  181. llms/extensions/providers/anthropic.py +0 -229
  182. llms/extensions/providers/chutes.py +0 -155
  183. llms/extensions/providers/google.py +0 -378
  184. llms/extensions/providers/nvidia.py +0 -105
  185. llms/extensions/providers/openai.py +0 -156
  186. llms/extensions/providers/openrouter.py +0 -72
  187. llms/extensions/system_prompts/README.md +0 -22
  188. llms/extensions/system_prompts/__init__.py +0 -45
  189. llms/extensions/system_prompts/__pycache__/__init__.cpython-314.pyc +0 -0
  190. llms/extensions/system_prompts/ui/index.mjs +0 -280
  191. llms/extensions/system_prompts/ui/prompts.json +0 -1067
  192. llms/extensions/tools/__init__.py +0 -5
  193. llms/extensions/tools/__pycache__/__init__.cpython-314.pyc +0 -0
  194. llms/extensions/tools/ui/index.mjs +0 -204
  195. llms/providers-extra.json +0 -356
  196. llms/ui/ctx.mjs +0 -365
  197. llms/ui/index.mjs +0 -129
  198. llms/ui/modules/chat/ChatBody.mjs +0 -691
  199. llms/ui/modules/chat/index.mjs +0 -828
  200. llms/ui/modules/layout.mjs +0 -243
  201. llms/ui/modules/model-selector.mjs +0 -851
  202. llms_py-3.0.0.dist-info/RECORD +0 -202
  203. {llms_py-3.0.0.dist-info → llms_py-3.0.0b1.dist-info}/WHEEL +0 -0
  204. {llms_py-3.0.0.dist-info → llms_py-3.0.0b1.dist-info}/entry_points.txt +0 -0
  205. {llms_py-3.0.0.dist-info → llms_py-3.0.0b1.dist-info}/licenses/LICENSE +0 -0
  206. {llms_py-3.0.0.dist-info → llms_py-3.0.0b1.dist-info}/top_level.txt +0 -0
@@ -1,18 +0,0 @@
1
- from .anthropic import install_anthropic
2
- from .chutes import install_chutes
3
- from .google import install_google
4
- from .nvidia import install_nvidia
5
- from .openai import install_openai
6
- from .openrouter import install_openrouter
7
-
8
-
9
- def install(ctx):
10
- install_anthropic(ctx)
11
- install_chutes(ctx)
12
- install_google(ctx)
13
- install_openai(ctx)
14
- install_openrouter(ctx)
15
- install_nvidia(ctx)
16
-
17
-
18
- __install__ = install
@@ -1,229 +0,0 @@
1
- import json
2
- import time
3
-
4
- import aiohttp
5
-
6
-
7
- def install_anthropic(ctx):
8
- from llms.main import OpenAiCompatible
9
-
10
- class AnthropicProvider(OpenAiCompatible):
11
- sdk = "@ai-sdk/anthropic"
12
-
13
- def __init__(self, **kwargs):
14
- if "api" not in kwargs:
15
- kwargs["api"] = "https://api.anthropic.com/v1"
16
- super().__init__(**kwargs)
17
-
18
- # Anthropic uses x-api-key header instead of Authorization
19
- if self.api_key:
20
- self.headers = self.headers.copy()
21
- if "Authorization" in self.headers:
22
- del self.headers["Authorization"]
23
- self.headers["x-api-key"] = self.api_key
24
-
25
- if "anthropic-version" not in self.headers:
26
- self.headers = self.headers.copy()
27
- self.headers["anthropic-version"] = "2023-06-01"
28
- self.chat_url = f"{self.api}/messages"
29
-
30
- async def chat(self, chat):
31
- chat["model"] = self.provider_model(chat["model"]) or chat["model"]
32
-
33
- chat = await self.process_chat(chat, provider_id=self.id)
34
-
35
- # Transform OpenAI format to Anthropic format
36
- anthropic_request = {
37
- "model": chat["model"],
38
- "messages": [],
39
- }
40
-
41
- # Extract system message (Anthropic uses top-level 'system' parameter)
42
- system_messages = []
43
- for message in chat.get("messages", []):
44
- if message.get("role") == "system":
45
- content = message.get("content", "")
46
- if isinstance(content, str):
47
- system_messages.append(content)
48
- elif isinstance(content, list):
49
- for item in content:
50
- if item.get("type") == "text":
51
- system_messages.append(item.get("text", ""))
52
-
53
- if system_messages:
54
- anthropic_request["system"] = "\n".join(system_messages)
55
-
56
- # Transform messages (exclude system messages)
57
- for message in chat.get("messages", []):
58
- if message.get("role") == "system":
59
- continue
60
-
61
- if message.get("role") == "tool":
62
- # Convert OpenAI tool response to Anthropic tool_result
63
- tool_call_id = message.get("tool_call_id")
64
- content = ctx.to_content(message.get("content", ""))
65
- if not isinstance(content, (str, list)):
66
- content = str(content)
67
-
68
- tool_result = {"type": "tool_result", "tool_use_id": tool_call_id, "content": content}
69
-
70
- # Anthropic requires tool results to be in a user message
71
- # Check if the last message was a user message, if so append to it
72
- if anthropic_request["messages"] and anthropic_request["messages"][-1]["role"] == "user":
73
- anthropic_request["messages"][-1]["content"].append(tool_result)
74
- else:
75
- anthropic_request["messages"].append({"role": "user", "content": [tool_result]})
76
- continue
77
-
78
- anthropic_message = {"role": message.get("role"), "content": []}
79
-
80
- content = message.get("content", "")
81
- if isinstance(content, str):
82
- anthropic_message["content"] = content
83
- elif isinstance(content, list):
84
- for item in content:
85
- if item.get("type") == "text":
86
- anthropic_message["content"].append({"type": "text", "text": item.get("text", "")})
87
- elif item.get("type") == "image_url" and "image_url" in item:
88
- # Transform OpenAI image_url format to Anthropic format
89
- image_url = item["image_url"].get("url", "")
90
- if image_url.startswith("data:"):
91
- # Extract media type and base64 data
92
- parts = image_url.split(";base64,", 1)
93
- if len(parts) == 2:
94
- media_type = parts[0].replace("data:", "")
95
- base64_data = parts[1]
96
- anthropic_message["content"].append(
97
- {
98
- "type": "image",
99
- "source": {"type": "base64", "media_type": media_type, "data": base64_data},
100
- }
101
- )
102
-
103
- anthropic_request["messages"].append(anthropic_message)
104
-
105
- # Handle max_tokens (required by Anthropic, uses max_tokens not max_completion_tokens)
106
- if "max_completion_tokens" in chat:
107
- anthropic_request["max_tokens"] = chat["max_completion_tokens"]
108
- elif "max_tokens" in chat:
109
- anthropic_request["max_tokens"] = chat["max_tokens"]
110
- else:
111
- # Anthropic requires max_tokens, set a default
112
- anthropic_request["max_tokens"] = 4096
113
-
114
- # Copy other supported parameters
115
- if "temperature" in chat:
116
- anthropic_request["temperature"] = chat["temperature"]
117
- if "top_p" in chat:
118
- anthropic_request["top_p"] = chat["top_p"]
119
- if "top_k" in chat:
120
- anthropic_request["top_k"] = chat["top_k"]
121
- if "stop" in chat:
122
- anthropic_request["stop_sequences"] = chat["stop"] if isinstance(chat["stop"], list) else [chat["stop"]]
123
- if "stream" in chat:
124
- anthropic_request["stream"] = chat["stream"]
125
- if "tools" in chat:
126
- anthropic_tools = []
127
- for tool in chat["tools"]:
128
- if tool.get("type") == "function":
129
- function = tool.get("function", {})
130
- anthropic_tool = {
131
- "name": function.get("name"),
132
- "description": function.get("description"),
133
- "input_schema": function.get("parameters"),
134
- }
135
- anthropic_tools.append(anthropic_tool)
136
- if anthropic_tools:
137
- anthropic_request["tools"] = anthropic_tools
138
- if "tool_choice" in chat:
139
- anthropic_request["tool_choice"] = chat["tool_choice"]
140
-
141
- ctx.log(f"POST {self.chat_url}")
142
- ctx.log(json.dumps(anthropic_request, indent=2))
143
-
144
- async with aiohttp.ClientSession() as session:
145
- started_at = time.time()
146
- async with session.post(
147
- self.chat_url,
148
- headers=self.headers,
149
- data=json.dumps(anthropic_request),
150
- timeout=aiohttp.ClientTimeout(total=120),
151
- ) as response:
152
- return ctx.log_json(self.to_response(await self.response_json(response), chat, started_at))
153
-
154
- def to_response(self, response, chat, started_at):
155
- """Convert Anthropic response format to OpenAI-compatible format."""
156
- # Transform Anthropic response to OpenAI format
157
- ret = {
158
- "id": response.get("id", ""),
159
- "object": "chat.completion",
160
- "created": int(started_at),
161
- "model": response.get("model", ""),
162
- "choices": [],
163
- "usage": {},
164
- }
165
-
166
- # Transform content blocks to message content
167
- content_parts = []
168
- thinking_parts = []
169
- tool_calls = []
170
-
171
- for block in response.get("content", []):
172
- if block.get("type") == "text":
173
- content_parts.append(block.get("text", ""))
174
- elif block.get("type") == "thinking":
175
- # Store thinking blocks separately (some models include reasoning)
176
- thinking_parts.append(block.get("thinking", ""))
177
- elif block.get("type") == "tool_use":
178
- tool_call = {
179
- "id": block.get("id"),
180
- "type": "function",
181
- "function": {
182
- "name": block.get("name"),
183
- "arguments": json.dumps(block.get("input", {})),
184
- },
185
- }
186
- tool_calls.append(tool_call)
187
-
188
- # Combine all text content
189
- message_content = "\n".join(content_parts) if content_parts else ""
190
-
191
- # Create the choice object
192
- choice = {
193
- "index": 0,
194
- "message": {"role": "assistant", "content": message_content},
195
- "finish_reason": response.get("stop_reason", "stop"),
196
- }
197
-
198
- # Add thinking as metadata if present
199
- if thinking_parts:
200
- choice["message"]["thinking"] = "\n".join(thinking_parts)
201
-
202
- # Add tool_calls if present
203
- if tool_calls:
204
- choice["message"]["tool_calls"] = tool_calls
205
-
206
- ret["choices"].append(choice)
207
-
208
- # Transform usage
209
- if "usage" in response:
210
- usage = response["usage"]
211
- ret["usage"] = {
212
- "prompt_tokens": usage.get("input_tokens", 0),
213
- "completion_tokens": usage.get("output_tokens", 0),
214
- "total_tokens": usage.get("input_tokens", 0) + usage.get("output_tokens", 0),
215
- }
216
-
217
- # Add metadata
218
- if "metadata" not in ret:
219
- ret["metadata"] = {}
220
- ret["metadata"]["duration"] = int((time.time() - started_at) * 1000)
221
-
222
- if chat is not None and "model" in chat:
223
- cost = self.model_cost(chat["model"])
224
- if cost and "input" in cost and "output" in cost:
225
- ret["metadata"]["pricing"] = f"{cost['input']}/{cost['output']}"
226
-
227
- return ret
228
-
229
- ctx.add_provider(AnthropicProvider)
@@ -1,155 +0,0 @@
1
- import json
2
- import mimetypes
3
- import time
4
-
5
- import aiohttp
6
-
7
-
8
- def install_chutes(ctx):
9
- from llms.main import GeneratorBase
10
-
11
- class ChutesImage(GeneratorBase):
12
- sdk = "chutes/image"
13
-
14
- def __init__(self, **kwargs):
15
- super().__init__(**kwargs)
16
- self.width = int(kwargs.get("width", 1024))
17
- self.height = int(kwargs.get("height", 1024))
18
- self.cfg_scale = float(kwargs.get("cfg_scale", 7.5))
19
- self.steps = int(kwargs.get("steps", 50))
20
- self.negative_prompt = kwargs.get("negative_prompt", "blur, distortion, low quality")
21
- self.gen_url = kwargs.get("api", "https://image.chutes.ai/generate")
22
- self.model_resolutions = {
23
- "chutes-hidream": {
24
- "1:1": "1024x1024",
25
- "9:16": "768x1360",
26
- "16:9": "1360x768",
27
- "3:4": "880x1168",
28
- "4:3": "1168x880",
29
- "2:3": "832x1248",
30
- "3:2": "1248x832",
31
- }
32
- }
33
- self.model_sizes = ["chutes-hunyuan-image-3"]
34
- self.model_negative_prompt = [
35
- "chroma",
36
- "qwen-image-edit-2509",
37
- "JuggernautXL-Ragnarok",
38
- "JuggernautXL",
39
- "Animij",
40
- "iLustMix",
41
- ]
42
-
43
- async def chat(self, chat, provider=None):
44
- headers = {"Authorization": f"Bearer {self.api_key}"}
45
- if provider is not None:
46
- headers["Authorization"] = f"Bearer {provider.api_key}"
47
- chat["model"] = provider.provider_model(chat["model"]) or chat["model"]
48
-
49
- aspect_ratio = "1:1"
50
- if "messages" in chat and len(chat["messages"]) > 0:
51
- aspect_ratio = chat["messages"][0].get("aspect_ratio", "1:1")
52
- cfg_scale = self.cfg_scale
53
- steps = self.steps
54
- width = self.width
55
- height = self.height
56
- if chat["model"] == "chutes-z-image-turbo":
57
- cfg_scale = min(self.cfg_scale, 5)
58
- payload = {
59
- "model": chat["model"],
60
- "prompt": ctx.last_user_prompt(chat),
61
- "guidance_scale": cfg_scale,
62
- "width": width,
63
- "height": height,
64
- "num_inference_steps": steps,
65
- }
66
- if chat["model"] in self.model_negative_prompt:
67
- payload["negative_prompt"] = self.negative_prompt
68
-
69
- image_config = chat.get("image_config", {})
70
- aspect_ratio = image_config.get("aspect_ratio")
71
- if aspect_ratio:
72
- dimension = ctx.app.aspect_ratios.get(aspect_ratio)
73
- if dimension:
74
- w, h = dimension.split("×")
75
- width, height = int(w), int(h)
76
- payload["width"] = width
77
- payload["height"] = height
78
-
79
- if chat["model"] in self.model_resolutions:
80
- # if models use resolution, remove width and height
81
- del payload["width"]
82
- del payload["height"]
83
- resolution = self.model_resolutions[chat["model"]][aspect_ratio]
84
- payload["resolution"] = resolution
85
- elif chat["model"] in self.model_sizes:
86
- del payload["width"]
87
- del payload["height"]
88
- payload["size"] = aspect_ratio
89
-
90
- gen_url = self.gen_url
91
- if chat["model"].startswith("chutes-"):
92
- model = payload["model"]
93
- gen_url = f"https://{model}.chutes.ai/generate"
94
- del payload["model"]
95
-
96
- ctx.log(f"POST {gen_url}")
97
- ctx.log(json.dumps(payload, indent=2))
98
- async with aiohttp.ClientSession() as session, session.post(
99
- gen_url, headers=headers, json=payload
100
- ) as response:
101
- if response.status < 300:
102
- image_data = await response.read()
103
- content_type = response.headers.get("Content-Type")
104
- if content_type:
105
- ext = mimetypes.guess_extension(content_type)
106
- if ext:
107
- ext = ext.lstrip(".") # remove leading dot
108
- if not ext:
109
- ext = "png"
110
-
111
- relative_url, info = ctx.save_image_to_cache(
112
- image_data,
113
- f"{chat['model']}.{ext}",
114
- ctx.to_file_info(
115
- chat,
116
- {
117
- "aspect_ratio": aspect_ratio,
118
- "width": width,
119
- "height": height,
120
- "cfg_scale": cfg_scale,
121
- "steps": steps,
122
- },
123
- ),
124
- )
125
- return {
126
- "choices": [
127
- {
128
- "message": {
129
- "role": "assistant",
130
- "content": self.default_content,
131
- "images": [
132
- {
133
- "type": "image_url",
134
- "image_url": {
135
- "url": relative_url,
136
- },
137
- }
138
- ],
139
- }
140
- }
141
- ],
142
- "created": int(time.time()),
143
- }
144
- else:
145
- text = await response.text()
146
- try:
147
- data = json.loads(text)
148
- ctx.log(data)
149
- if "detail" in data:
150
- raise Exception(data["detail"])
151
- except json.JSONDecodeError:
152
- pass
153
- raise Exception(f"Failed to generate image {response.status}")
154
-
155
- ctx.add_provider(ChutesImage)