llms-py 3.0.0b10__py3-none-any.whl → 3.0.2__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.
- llms/{extensions/app/db_manager.py → db.py} +170 -15
- llms/extensions/app/__init__.py +95 -39
- llms/extensions/app/db.py +16 -124
- llms/extensions/app/ui/threadStore.mjs +20 -2
- llms/extensions/core_tools/__init__.py +37 -0
- llms/extensions/gallery/__init__.py +15 -13
- llms/extensions/gallery/db.py +117 -172
- llms/extensions/gallery/ui/index.mjs +1 -1
- llms/extensions/providers/__init__.py +3 -1
- llms/extensions/providers/anthropic.py +7 -3
- llms/extensions/providers/cerebras.py +37 -0
- llms/extensions/providers/chutes.py +1 -1
- llms/extensions/providers/google.py +131 -28
- llms/extensions/providers/nvidia.py +2 -2
- llms/extensions/providers/openai.py +2 -2
- llms/extensions/providers/openrouter.py +4 -2
- llms/extensions/system_prompts/ui/index.mjs +21 -26
- llms/extensions/system_prompts/ui/prompts.json +5 -5
- llms/llms.json +3 -0
- llms/main.py +81 -34
- llms/providers.json +1 -1
- llms/ui/ai.mjs +1 -1
- llms/ui/app.css +96 -3
- llms/ui/ctx.mjs +24 -1
- llms/ui/index.mjs +2 -0
- llms/ui/modules/chat/ChatBody.mjs +1 -0
- llms/ui/modules/chat/index.mjs +19 -1
- llms/ui/modules/icons.mjs +46 -0
- llms/ui/modules/layout.mjs +28 -0
- llms/ui/modules/model-selector.mjs +0 -40
- llms/ui/utils.mjs +9 -1
- {llms_py-3.0.0b10.dist-info → llms_py-3.0.2.dist-info}/METADATA +1 -1
- {llms_py-3.0.0b10.dist-info → llms_py-3.0.2.dist-info}/RECORD +37 -65
- llms/__pycache__/__init__.cpython-312.pyc +0 -0
- llms/__pycache__/__init__.cpython-313.pyc +0 -0
- llms/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/__pycache__/__main__.cpython-312.pyc +0 -0
- llms/__pycache__/__main__.cpython-314.pyc +0 -0
- llms/__pycache__/llms.cpython-312.pyc +0 -0
- llms/__pycache__/main.cpython-312.pyc +0 -0
- llms/__pycache__/main.cpython-313.pyc +0 -0
- llms/__pycache__/main.cpython-314.pyc +0 -0
- llms/__pycache__/plugins.cpython-314.pyc +0 -0
- llms/extensions/app/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/app/__pycache__/db.cpython-314.pyc +0 -0
- llms/extensions/app/__pycache__/db_manager.cpython-314.pyc +0 -0
- llms/extensions/app/requests.json +0 -9073
- llms/extensions/app/threads.json +0 -15290
- llms/extensions/core_tools/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/core_tools/ui/codemirror/lib/codemirror.css +0 -344
- llms/extensions/core_tools/ui/codemirror/lib/codemirror.js +0 -9884
- llms/extensions/gallery/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/gallery/__pycache__/db.cpython-314.pyc +0 -0
- llms/extensions/katex/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/anthropic.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/chutes.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/google.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/nvidia.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/openai.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/openrouter.cpython-314.pyc +0 -0
- llms/extensions/system_prompts/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/tools/__pycache__/__init__.cpython-314.pyc +0 -0
- {llms_py-3.0.0b10.dist-info → llms_py-3.0.2.dist-info}/WHEEL +0 -0
- {llms_py-3.0.0b10.dist-info → llms_py-3.0.2.dist-info}/entry_points.txt +0 -0
- {llms_py-3.0.0b10.dist-info → llms_py-3.0.2.dist-info}/licenses/LICENSE +0 -0
- {llms_py-3.0.0b10.dist-info → llms_py-3.0.2.dist-info}/top_level.txt +0 -0
llms/extensions/gallery/db.py
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
|
-
import
|
|
1
|
+
import datetime
|
|
2
2
|
import os
|
|
3
|
-
import sqlite3
|
|
4
3
|
from typing import Any, Dict
|
|
5
4
|
|
|
5
|
+
from llms.db import DbManager, order_by, to_dto
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def with_user(data, user):
|
|
9
|
+
if user is None:
|
|
10
|
+
if "user" in data:
|
|
11
|
+
del data["user"]
|
|
12
|
+
return data
|
|
13
|
+
else:
|
|
14
|
+
data["user"] = user
|
|
15
|
+
return data
|
|
16
|
+
|
|
6
17
|
|
|
7
18
|
def ratio_format(ratio):
|
|
8
19
|
w, h = ratio.split(":")
|
|
@@ -20,7 +31,13 @@ class GalleryDB:
|
|
|
20
31
|
|
|
21
32
|
self.ctx = ctx
|
|
22
33
|
self.db_path = str(db_path)
|
|
34
|
+
dirname = os.path.dirname(self.db_path)
|
|
35
|
+
if dirname:
|
|
36
|
+
os.makedirs(dirname, exist_ok=True)
|
|
37
|
+
|
|
38
|
+
self.db = DbManager(ctx, self.db_path)
|
|
23
39
|
self.columns = {
|
|
40
|
+
"id": "INTEGER",
|
|
24
41
|
"name": "TEXT", # chutes-hunyuan-image-3.png (filename)
|
|
25
42
|
"type": "TEXT", # image|audio|video
|
|
26
43
|
"prompt": "TEXT",
|
|
@@ -59,7 +76,8 @@ class GalleryDB:
|
|
|
59
76
|
"landscape": [ratio for ratio in ratios if ratio_format(ratio) == 1],
|
|
60
77
|
"portrait": [ratio for ratio in ratios if ratio_format(ratio) == -1],
|
|
61
78
|
}
|
|
62
|
-
self.
|
|
79
|
+
with self.db.create_writer_connection() as conn:
|
|
80
|
+
self.init_db(conn)
|
|
63
81
|
|
|
64
82
|
def closest_aspect_ratio(self, width, height):
|
|
65
83
|
target_ratio = width / height
|
|
@@ -76,172 +94,108 @@ class GalleryDB:
|
|
|
76
94
|
return closest_ratio
|
|
77
95
|
|
|
78
96
|
def get_connection(self):
|
|
79
|
-
return
|
|
97
|
+
return self.db.create_reader_connection()
|
|
80
98
|
|
|
81
|
-
def
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
os.makedirs(dirname, exist_ok=True)
|
|
94
|
-
with self.get_connection() as conn:
|
|
95
|
-
# Create table with all columns
|
|
96
|
-
self.exec(
|
|
97
|
-
conn,
|
|
98
|
-
"""
|
|
99
|
-
CREATE TABLE IF NOT EXISTS media (
|
|
100
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
101
|
-
name TEXT,
|
|
102
|
-
type TEXT NOT NULL,
|
|
103
|
-
prompt TEXT,
|
|
104
|
-
model TEXT,
|
|
105
|
-
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
106
|
-
cost REAL,
|
|
107
|
-
seed INTEGER,
|
|
108
|
-
url TEXT NOT NULL UNIQUE,
|
|
109
|
-
hash TEXT NOT NULL UNIQUE,
|
|
110
|
-
aspect_ratio TEXT,
|
|
111
|
-
width INTEGER,
|
|
112
|
-
height INTEGER,
|
|
113
|
-
size INTEGER,
|
|
114
|
-
duration INTEGER,
|
|
115
|
-
user TEXT,
|
|
116
|
-
reactions JSON,
|
|
117
|
-
caption TEXT,
|
|
118
|
-
description TEXT,
|
|
119
|
-
phash TEXT,
|
|
120
|
-
color TEXT,
|
|
121
|
-
category JSON,
|
|
122
|
-
tags JSON,
|
|
123
|
-
rating TEXT,
|
|
124
|
-
ratings JSON,
|
|
125
|
-
objects JSON,
|
|
126
|
-
variantId TEXT,
|
|
127
|
-
variantName TEXT,
|
|
128
|
-
published TIMESTAMP,
|
|
129
|
-
metadata JSON
|
|
130
|
-
)
|
|
131
|
-
""",
|
|
99
|
+
def init_db(self, conn):
|
|
100
|
+
# Create table with all columns
|
|
101
|
+
overrides = {
|
|
102
|
+
"id": "INTEGER PRIMARY KEY AUTOINCREMENT",
|
|
103
|
+
"created": "TIMESTAMP DEFAULT CURRENT_TIMESTAMP",
|
|
104
|
+
}
|
|
105
|
+
sql_columns = ",".join([f"{col} {overrides.get(col, dtype)}" for col, dtype in self.columns.items()])
|
|
106
|
+
self.db.exec(
|
|
107
|
+
conn,
|
|
108
|
+
f"""
|
|
109
|
+
CREATE TABLE IF NOT EXISTS media (
|
|
110
|
+
{sql_columns}
|
|
132
111
|
)
|
|
112
|
+
""",
|
|
113
|
+
)
|
|
133
114
|
|
|
134
|
-
|
|
135
|
-
|
|
115
|
+
self.db.exec(conn, "CREATE INDEX IF NOT EXISTS idx_media_user ON media(user)")
|
|
116
|
+
self.db.exec(conn, "CREATE INDEX IF NOT EXISTS idx_media_type ON media(type)")
|
|
136
117
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
118
|
+
# Check for missing columns and migrate if necessary
|
|
119
|
+
cur = self.db.exec(conn, "PRAGMA table_info(media)")
|
|
120
|
+
columns = {row[1] for row in cur.fetchall()}
|
|
140
121
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
122
|
+
for col, dtype in self.columns.items():
|
|
123
|
+
if col not in columns:
|
|
124
|
+
try:
|
|
125
|
+
self.db.exec(conn, f"ALTER TABLE media ADD COLUMN {col} {dtype}")
|
|
126
|
+
except Exception as e:
|
|
127
|
+
self.ctx.err(f"adding column {col}", e)
|
|
147
128
|
|
|
148
|
-
def
|
|
149
|
-
|
|
150
|
-
if not info:
|
|
151
|
-
raise Exception("info is required")
|
|
152
|
-
|
|
153
|
-
# Helper to safely dump JSON if value exists
|
|
154
|
-
def db_value(val):
|
|
155
|
-
if val is None or val == "":
|
|
156
|
-
return None
|
|
157
|
-
if isinstance(val, (dict, list)):
|
|
158
|
-
return json.dumps(val)
|
|
159
|
-
return val
|
|
160
|
-
|
|
161
|
-
meta = {}
|
|
162
|
-
metadata = {}
|
|
163
|
-
known_columns = self.columns.keys()
|
|
164
|
-
for k in known_columns:
|
|
165
|
-
val = info.get(k, None)
|
|
166
|
-
if k == "metadata":
|
|
167
|
-
continue
|
|
168
|
-
if k == "created" and not val:
|
|
169
|
-
continue
|
|
170
|
-
if k == "type":
|
|
171
|
-
parts = val.split("/")
|
|
172
|
-
if parts[0] == "image" or parts[0] == "video" or parts[0] == "audio":
|
|
173
|
-
meta[k] = parts[0]
|
|
174
|
-
else:
|
|
175
|
-
meta[k] = db_value(val)
|
|
176
|
-
# for items not in known_columns, add to metadata
|
|
177
|
-
for k in info:
|
|
178
|
-
if k not in known_columns:
|
|
179
|
-
metadata[k] = info[k]
|
|
180
|
-
|
|
181
|
-
if not meta.get("hash"):
|
|
182
|
-
meta["hash"] = meta["url"].split("/")[-1].split(".")[0]
|
|
183
|
-
|
|
184
|
-
if "width" in meta and "height" in meta and meta["width"] and meta["height"]:
|
|
185
|
-
meta["aspect_ratio"] = self.closest_aspect_ratio(int(meta["width"]), int(meta["height"]))
|
|
186
|
-
|
|
187
|
-
meta["metadata"] = db_value(metadata)
|
|
188
|
-
|
|
189
|
-
with self.get_connection() as conn:
|
|
190
|
-
insert_keys = list(meta.keys())
|
|
191
|
-
insert_body = ", ".join(insert_keys)
|
|
192
|
-
insert_values = ", ".join(["?" for _ in insert_keys])
|
|
193
|
-
|
|
194
|
-
self.exec(
|
|
195
|
-
conn,
|
|
196
|
-
f"""
|
|
197
|
-
INSERT INTO media (
|
|
198
|
-
{insert_body}
|
|
199
|
-
)
|
|
200
|
-
VALUES ({insert_values})
|
|
201
|
-
""",
|
|
202
|
-
tuple(meta[k] for k in insert_keys),
|
|
203
|
-
)
|
|
204
|
-
except sqlite3.IntegrityError as e:
|
|
205
|
-
# unique constraint failed, file already exists.
|
|
206
|
-
self.ctx.dbg(f"media already exists {e}")
|
|
207
|
-
except Exception as e:
|
|
208
|
-
self.ctx.err("insert media", e)
|
|
129
|
+
def to_dto(self, row, json_columns):
|
|
130
|
+
return to_dto(self.ctx, row, json_columns)
|
|
209
131
|
|
|
210
|
-
def get_user_filter(self, user=None):
|
|
132
|
+
def get_user_filter(self, user=None, params=None):
|
|
211
133
|
if user is None:
|
|
212
|
-
return "WHERE user IS NULL
|
|
134
|
+
return "WHERE user IS NULL", params or {}
|
|
135
|
+
else:
|
|
136
|
+
args = params.copy() if params else {}
|
|
137
|
+
args.update({"user": user})
|
|
138
|
+
return "WHERE user = :user", args
|
|
139
|
+
|
|
140
|
+
def prepare_media(self, media, id=None, user=None):
|
|
141
|
+
now = datetime.now()
|
|
142
|
+
if id:
|
|
143
|
+
media["id"] = id
|
|
213
144
|
else:
|
|
214
|
-
|
|
145
|
+
media["created"] = now
|
|
146
|
+
return with_user(media, user=user)
|
|
147
|
+
|
|
148
|
+
def insert_media(self, info, user=None, callback=None):
|
|
149
|
+
if not info:
|
|
150
|
+
raise Exception("info is required")
|
|
151
|
+
|
|
152
|
+
media = {}
|
|
153
|
+
metadata = {}
|
|
154
|
+
known_columns = self.columns.keys()
|
|
155
|
+
for k in known_columns:
|
|
156
|
+
val = info.get(k, None)
|
|
157
|
+
if k == "metadata":
|
|
158
|
+
continue
|
|
159
|
+
if k == "created" and not val:
|
|
160
|
+
continue
|
|
161
|
+
if k == "type":
|
|
162
|
+
parts = val.split("/")
|
|
163
|
+
if parts[0] == "image" or parts[0] == "video" or parts[0] == "audio":
|
|
164
|
+
media[k] = parts[0]
|
|
165
|
+
else:
|
|
166
|
+
media[k] = self.db.value(val)
|
|
167
|
+
# for items not in known_columns, add to metadata
|
|
168
|
+
for k in info:
|
|
169
|
+
if k not in known_columns:
|
|
170
|
+
metadata[k] = info[k]
|
|
171
|
+
|
|
172
|
+
if not media.get("hash"):
|
|
173
|
+
media["hash"] = media["url"].split("/")[-1].split(".")[0]
|
|
174
|
+
|
|
175
|
+
if "width" in media and "height" in media and media["width"] and media["height"]:
|
|
176
|
+
media["aspect_ratio"] = self.closest_aspect_ratio(int(media["width"]), int(media["height"]))
|
|
177
|
+
|
|
178
|
+
media["metadata"] = self.db.value(metadata)
|
|
179
|
+
media = with_user(media, user=user)
|
|
180
|
+
|
|
181
|
+
insert_keys = list(media.keys())
|
|
182
|
+
insert_body = ", ".join(insert_keys)
|
|
183
|
+
insert_values = ", ".join(["?" for _ in insert_keys])
|
|
184
|
+
|
|
185
|
+
sql = f"INSERT INTO media ({insert_body}) VALUES ({insert_values})"
|
|
186
|
+
|
|
187
|
+
self.db.write(sql, tuple(media[k] for k in insert_keys), callback)
|
|
215
188
|
|
|
216
189
|
def media_totals(self, user=None):
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
f"SELECT type, COUNT(*) as count FROM media {sql_where} GROUP BY type ORDER BY count DESC",
|
|
223
|
-
params,
|
|
224
|
-
)
|
|
225
|
-
except Exception as e:
|
|
226
|
-
self.ctx.err("media_totals", e)
|
|
227
|
-
return []
|
|
228
|
-
|
|
229
|
-
def all_media(self, limit=100, offset=0, user=None):
|
|
230
|
-
try:
|
|
231
|
-
with self.get_connection() as conn:
|
|
232
|
-
sql_where, params = self.get_user_filter(user)
|
|
233
|
-
params.update({"limit": limit, "offset": offset})
|
|
234
|
-
return self.all(
|
|
235
|
-
conn,
|
|
236
|
-
f"SELECT * FROM media {sql_where} ORDER BY id DESC LIMIT :limit OFFSET :offset",
|
|
237
|
-
params,
|
|
238
|
-
)
|
|
239
|
-
except Exception as e:
|
|
240
|
-
self.ctx.err(f"all_media ({limit}, {offset})", e)
|
|
241
|
-
return []
|
|
190
|
+
sql_where, params = self.get_user_filter(user)
|
|
191
|
+
return self.db.all(
|
|
192
|
+
f"SELECT type, COUNT(*) as count FROM media {sql_where} GROUP BY type ORDER BY count DESC",
|
|
193
|
+
params,
|
|
194
|
+
)
|
|
242
195
|
|
|
243
196
|
def query_media(self, query: Dict[str, Any], user=None):
|
|
244
197
|
try:
|
|
198
|
+
all_columns = self.columns.keys()
|
|
245
199
|
take = query.get("take", 50)
|
|
246
200
|
skip = query.get("skip", 0)
|
|
247
201
|
sort = query.get("sort", "-id")
|
|
@@ -275,24 +229,15 @@ class GalleryDB:
|
|
|
275
229
|
ratios = ", ".join([f"'{ratio}'" for ratio in format_ratios])
|
|
276
230
|
sql_where += f"aspect_ratio IN ({ratios})"
|
|
277
231
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
return self.all(
|
|
283
|
-
conn,
|
|
284
|
-
f"SELECT * FROM media {sql_where} {sql_orderby} LIMIT :take OFFSET :skip",
|
|
285
|
-
params,
|
|
286
|
-
)
|
|
232
|
+
return self.db.all(
|
|
233
|
+
f"SELECT * FROM media {sql_where} {order_by(all_columns, sort)} LIMIT :take OFFSET :skip",
|
|
234
|
+
params,
|
|
235
|
+
)
|
|
287
236
|
except Exception as e:
|
|
288
237
|
self.ctx.err(f"query_media ({take}, {skip})", e)
|
|
289
238
|
return []
|
|
290
239
|
|
|
291
|
-
def delete_media(self, hash, user=None):
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
params.update({"hash": hash})
|
|
296
|
-
self.exec(conn, f"DELETE FROM media {sql_where} AND hash = :hash", params)
|
|
297
|
-
except Exception as e:
|
|
298
|
-
self.ctx.err(f"delete_media ({hash})", e)
|
|
240
|
+
def delete_media(self, hash, user=None, callback=None):
|
|
241
|
+
sql_where, params = self.get_user_filter(user)
|
|
242
|
+
params.update({"hash": hash})
|
|
243
|
+
self.db.write(f"DELETE FROM media {sql_where} AND hash = :hash", params, callback)
|
|
@@ -471,7 +471,7 @@ export default {
|
|
|
471
471
|
ctx.setLeftIcons({
|
|
472
472
|
gallery: {
|
|
473
473
|
component: {
|
|
474
|
-
template: `<svg @click="$ctx.togglePath('/gallery')" viewBox="0 0 15 15"
|
|
474
|
+
template: `<svg @click="$ctx.togglePath('/gallery')" viewBox="0 0 15 15"><path fill="currentColor" d="M10.71 3L7.85.15a.5.5 0 0 0-.707-.003L7.14.15L4.29 3H1.5a.5.5 0 0 0-.5.5v9a.5.5 0 0 0 .5.5h12a.5.5 0 0 0 .5-.5v-9a.5.5 0 0 0-.5-.5zM7.5 1.21L9.29 3H5.71zM13 12H2V4h11zM5 7a1 1 0 1 1 0-2a1 1 0 0 1 0 2m7 4H4.5L6 8l1.25 2.5L9.5 6z"/></svg>`,
|
|
475
475
|
},
|
|
476
476
|
isActive({ path }) { return path === '/gallery' }
|
|
477
477
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from .anthropic import install_anthropic
|
|
2
|
+
from .cerebras import install_cerebras
|
|
2
3
|
from .chutes import install_chutes
|
|
3
4
|
from .google import install_google
|
|
4
5
|
from .nvidia import install_nvidia
|
|
@@ -8,11 +9,12 @@ from .openrouter import install_openrouter
|
|
|
8
9
|
|
|
9
10
|
def install(ctx):
|
|
10
11
|
install_anthropic(ctx)
|
|
12
|
+
install_cerebras(ctx)
|
|
11
13
|
install_chutes(ctx)
|
|
12
14
|
install_google(ctx)
|
|
15
|
+
install_nvidia(ctx)
|
|
13
16
|
install_openai(ctx)
|
|
14
17
|
install_openrouter(ctx)
|
|
15
|
-
install_nvidia(ctx)
|
|
16
18
|
|
|
17
19
|
|
|
18
20
|
__install__ = install
|
|
@@ -27,7 +27,7 @@ def install_anthropic(ctx):
|
|
|
27
27
|
self.headers["anthropic-version"] = "2023-06-01"
|
|
28
28
|
self.chat_url = f"{self.api}/messages"
|
|
29
29
|
|
|
30
|
-
async def chat(self, chat):
|
|
30
|
+
async def chat(self, chat, context=None):
|
|
31
31
|
chat["model"] = self.provider_model(chat["model"]) or chat["model"]
|
|
32
32
|
|
|
33
33
|
chat = await self.process_chat(chat, provider_id=self.id)
|
|
@@ -149,10 +149,14 @@ def install_anthropic(ctx):
|
|
|
149
149
|
data=json.dumps(anthropic_request),
|
|
150
150
|
timeout=aiohttp.ClientTimeout(total=120),
|
|
151
151
|
) as response:
|
|
152
|
-
return ctx.log_json(
|
|
152
|
+
return ctx.log_json(
|
|
153
|
+
self.to_response(await self.response_json(response), chat, started_at, context=context)
|
|
154
|
+
)
|
|
153
155
|
|
|
154
|
-
def to_response(self, response, chat, started_at):
|
|
156
|
+
def to_response(self, response, chat, started_at, context=None):
|
|
155
157
|
"""Convert Anthropic response format to OpenAI-compatible format."""
|
|
158
|
+
if context is not None:
|
|
159
|
+
context["providerResponse"] = response
|
|
156
160
|
# Transform Anthropic response to OpenAI format
|
|
157
161
|
ret = {
|
|
158
162
|
"id": response.get("id", ""),
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
def install_cerebras(ctx):
|
|
2
|
+
from llms.main import OpenAiCompatible
|
|
3
|
+
|
|
4
|
+
class CerebrasProvider(OpenAiCompatible):
|
|
5
|
+
sdk = "@ai-sdk/cerebras"
|
|
6
|
+
|
|
7
|
+
def __init__(self, **kwargs):
|
|
8
|
+
if "api" not in kwargs:
|
|
9
|
+
kwargs["api"] = "https://api.cerebras.ai/v1"
|
|
10
|
+
super().__init__(**kwargs)
|
|
11
|
+
|
|
12
|
+
async def chat(self, chat, context=None):
|
|
13
|
+
# Cerebras only supports string content for text-only models
|
|
14
|
+
clean_chat = chat.copy()
|
|
15
|
+
clean_chat["messages"] = []
|
|
16
|
+
for msg in chat.get("messages", []):
|
|
17
|
+
new_msg = msg.copy()
|
|
18
|
+
content = msg.get("content")
|
|
19
|
+
if isinstance(content, list):
|
|
20
|
+
# Check if text only
|
|
21
|
+
is_text_only = True
|
|
22
|
+
text_parts = []
|
|
23
|
+
for part in content:
|
|
24
|
+
if part.get("type") != "text":
|
|
25
|
+
is_text_only = False
|
|
26
|
+
break
|
|
27
|
+
text_parts.append(part.get("text", ""))
|
|
28
|
+
|
|
29
|
+
if is_text_only:
|
|
30
|
+
new_msg["content"] = "".join(text_parts)
|
|
31
|
+
clean_chat["messages"].append(new_msg)
|
|
32
|
+
|
|
33
|
+
clean_chat.pop("modalities", None)
|
|
34
|
+
clean_chat.pop("systemPrompt", None)
|
|
35
|
+
return await super().chat(clean_chat, context)
|
|
36
|
+
|
|
37
|
+
ctx.add_provider(CerebrasProvider)
|
|
@@ -40,7 +40,7 @@ def install_chutes(ctx):
|
|
|
40
40
|
"iLustMix",
|
|
41
41
|
]
|
|
42
42
|
|
|
43
|
-
async def chat(self, chat, provider=None):
|
|
43
|
+
async def chat(self, chat, provider=None, context=None):
|
|
44
44
|
headers = {"Authorization": f"Bearer {self.api_key}"}
|
|
45
45
|
if provider is not None:
|
|
46
46
|
headers["Authorization"] = f"Bearer {provider.api_key}"
|