cobalt-cli-linux 0.1.5__tar.gz → 0.1.7__tar.gz
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.
- {cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/PKG-INFO +1 -1
- {cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/pyproject.toml +1 -1
- {cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/src/cobalt_cli_linux/agent.py +3 -0
- {cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/src/cobalt_cli_linux/groq_client.py +6 -2
- {cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/src/cobalt_cli_linux/tui.py +132 -3
- {cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/src/cobalt_cli_linux.egg-info/PKG-INFO +1 -1
- {cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/tests/test_cli.py +71 -0
- {cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/README.md +0 -0
- {cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/setup.cfg +0 -0
- {cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/src/cobalt_cli_linux/ASCII.txt +0 -0
- {cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/src/cobalt_cli_linux/__init__.py +0 -0
- {cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/src/cobalt_cli_linux/__main__.py +0 -0
- {cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/src/cobalt_cli_linux/cli.py +0 -0
- {cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/src/cobalt_cli_linux/config.py +0 -0
- {cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/src/cobalt_cli_linux/deepseek_client.py +0 -0
- {cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/src/cobalt_cli_linux/executor.py +0 -0
- {cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/src/cobalt_cli_linux/history.py +0 -0
- {cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/src/cobalt_cli_linux/profiles.py +0 -0
- {cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/src/cobalt_cli_linux.egg-info/SOURCES.txt +0 -0
- {cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/src/cobalt_cli_linux.egg-info/dependency_links.txt +0 -0
- {cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/src/cobalt_cli_linux.egg-info/entry_points.txt +0 -0
- {cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/src/cobalt_cli_linux.egg-info/requires.txt +0 -0
- {cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/src/cobalt_cli_linux.egg-info/top_level.txt +0 -0
|
@@ -169,6 +169,9 @@ class CobaltAgent:
|
|
|
169
169
|
on_chunk(chunk)
|
|
170
170
|
buffer = follow_up_buffer
|
|
171
171
|
|
|
172
|
+
if not buffer.strip():
|
|
173
|
+
buffer = "I could not produce a visible response for that request. Please try again."
|
|
174
|
+
|
|
172
175
|
if self.history.messages and self.history.messages[-1].get("role") == "assistant":
|
|
173
176
|
self.history.messages[-1]["content"] = buffer
|
|
174
177
|
if thinking_buffer:
|
|
@@ -16,6 +16,7 @@ class GroqStreamEvent(dict[str, str]):
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class GroqClient:
|
|
19
|
+
MAX_MODEL_TOKENS = 512
|
|
19
20
|
DEFAULT_MODELS: ClassVar[list[str]] = [
|
|
20
21
|
"llama-3.3-70b-versatile",
|
|
21
22
|
"llama-3.1-8b-instant",
|
|
@@ -46,6 +47,9 @@ class GroqClient:
|
|
|
46
47
|
lowered = model_id.lower()
|
|
47
48
|
return not any(token in lowered for token in self.NON_CHAT_TOKENS)
|
|
48
49
|
|
|
50
|
+
def _safe_max_tokens(self, max_tokens: int) -> int:
|
|
51
|
+
return max(1, min(max_tokens, self.MAX_MODEL_TOKENS))
|
|
52
|
+
|
|
49
53
|
def resolve_model(self) -> str:
|
|
50
54
|
if self.model and self.model != "auto":
|
|
51
55
|
return self.model
|
|
@@ -85,7 +89,7 @@ class GroqClient:
|
|
|
85
89
|
"model": resolved_model,
|
|
86
90
|
"messages": messages,
|
|
87
91
|
"temperature": temperature,
|
|
88
|
-
"max_tokens": max_tokens,
|
|
92
|
+
"max_tokens": self._safe_max_tokens(max_tokens),
|
|
89
93
|
}
|
|
90
94
|
headers = {
|
|
91
95
|
"Authorization": f"Bearer {self.api_key}",
|
|
@@ -135,7 +139,7 @@ class GroqClient:
|
|
|
135
139
|
"model": resolved_model,
|
|
136
140
|
"messages": messages,
|
|
137
141
|
"temperature": temperature,
|
|
138
|
-
"max_tokens": max_tokens,
|
|
142
|
+
"max_tokens": self._safe_max_tokens(max_tokens),
|
|
139
143
|
"stream": True,
|
|
140
144
|
}
|
|
141
145
|
headers = {
|
|
@@ -2,6 +2,8 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import curses
|
|
4
4
|
import re
|
|
5
|
+
import shutil
|
|
6
|
+
import subprocess
|
|
5
7
|
import textwrap
|
|
6
8
|
from pathlib import Path
|
|
7
9
|
|
|
@@ -30,6 +32,7 @@ class CobaltTUI:
|
|
|
30
32
|
self.auth_view: str | None = None
|
|
31
33
|
self.auth_field = 0
|
|
32
34
|
self.auth_values = ["", ""]
|
|
35
|
+
self.scroll_offset = 0
|
|
33
36
|
|
|
34
37
|
def _load_ascii_art(self) -> list[str]:
|
|
35
38
|
ascii_path = Path(__file__).resolve().parents[2] / "ASCII.txt"
|
|
@@ -91,6 +94,7 @@ class CobaltTUI:
|
|
|
91
94
|
self.history = self.current_chat
|
|
92
95
|
self.selected_chat_path = str(self.current_chat.path)
|
|
93
96
|
self.chat_files = self.chat_store.list_chats()
|
|
97
|
+
self.scroll_offset = 0
|
|
94
98
|
|
|
95
99
|
def _delete_selected_chat(self) -> None:
|
|
96
100
|
if not self.selected_chat_path:
|
|
@@ -104,6 +108,7 @@ class CobaltTUI:
|
|
|
104
108
|
self.selected_chat_path = str(self.chat_files[-1])
|
|
105
109
|
self.history = self.chat_store.load_chat(self.selected_chat_path)
|
|
106
110
|
self.current_chat = self.history
|
|
111
|
+
self.scroll_offset = 0
|
|
107
112
|
else:
|
|
108
113
|
self._start_new_chat()
|
|
109
114
|
|
|
@@ -141,7 +146,7 @@ class CobaltTUI:
|
|
|
141
146
|
|
|
142
147
|
def _input_box_geometry(self, main_x: int, main_y: int, main_w: int, main_h: int, input_text: str) -> tuple[int, int, int, int, list[str]]:
|
|
143
148
|
box_width = max(20, main_w - 4)
|
|
144
|
-
text_width = max(10, box_width - 6)
|
|
149
|
+
text_width = max(10, box_width - 6 - len("[ Copy prompt ]") - 2)
|
|
145
150
|
input_lines = textwrap.wrap(
|
|
146
151
|
input_text,
|
|
147
152
|
width=text_width,
|
|
@@ -167,8 +172,56 @@ class CobaltTUI:
|
|
|
167
172
|
for index, line in enumerate(input_lines):
|
|
168
173
|
prompt = "> " if index == 0 else " "
|
|
169
174
|
stdscr.addstr(box_y + 1 + index, box_x, f"{prompt}{line}"[:box_width])
|
|
175
|
+
copy_label = "[ Copy prompt ]"
|
|
176
|
+
copy_x = max(box_x + 4, box_x + box_width - len(copy_label) - 2)
|
|
177
|
+
paste_label = "[ Paste ]"
|
|
178
|
+
paste_x = max(box_x + 4, copy_x - len(paste_label) - 2)
|
|
179
|
+
stdscr.addstr(box_y + 1, paste_x, paste_label[: max(0, copy_x - paste_x - 2)], curses.A_DIM)
|
|
180
|
+
stdscr.addstr(box_y + 1, copy_x, copy_label[: max(0, box_width - (copy_x - box_x))], curses.A_DIM)
|
|
170
181
|
stdscr.addstr(box_y + box_height - 1, box_x, bottom[:box_width], curses.A_BOLD if focused else curses.A_DIM)
|
|
171
182
|
|
|
183
|
+
def _copy_to_clipboard(self, text: str) -> str:
|
|
184
|
+
if not text:
|
|
185
|
+
raise ValueError("Nothing to copy")
|
|
186
|
+
for command in ("wl-copy", "xclip", "xsel"):
|
|
187
|
+
executable = shutil.which(command)
|
|
188
|
+
if not executable:
|
|
189
|
+
continue
|
|
190
|
+
args = [executable]
|
|
191
|
+
if command == "xclip":
|
|
192
|
+
args.extend(["-selection", "clipboard"])
|
|
193
|
+
elif command == "xsel":
|
|
194
|
+
args.extend(["--clipboard", "--input"])
|
|
195
|
+
try:
|
|
196
|
+
subprocess.run(args, input=text, text=True, check=True)
|
|
197
|
+
except (OSError, subprocess.CalledProcessError):
|
|
198
|
+
continue
|
|
199
|
+
return "Copied"
|
|
200
|
+
raise RuntimeError("No clipboard utility found (install wl-clipboard, xclip, or xsel)")
|
|
201
|
+
|
|
202
|
+
def _paste_from_clipboard(self) -> str:
|
|
203
|
+
for command in ("wl-paste", "xclip", "xsel"):
|
|
204
|
+
executable = shutil.which(command)
|
|
205
|
+
if not executable:
|
|
206
|
+
continue
|
|
207
|
+
args = [executable]
|
|
208
|
+
if command == "xclip":
|
|
209
|
+
args.extend(["-selection", "clipboard", "-o"])
|
|
210
|
+
elif command == "xsel":
|
|
211
|
+
args.extend(["--clipboard", "--output"])
|
|
212
|
+
try:
|
|
213
|
+
result = subprocess.run(args, capture_output=True, text=True, check=True)
|
|
214
|
+
except (OSError, subprocess.CalledProcessError):
|
|
215
|
+
continue
|
|
216
|
+
return result.stdout.replace("\r\n", "\n").replace("\r", "\n")
|
|
217
|
+
raise RuntimeError("No working clipboard utility found (install wl-clipboard, xclip, or xsel)")
|
|
218
|
+
|
|
219
|
+
def _latest_response(self) -> str:
|
|
220
|
+
for message in reversed(self.history.messages):
|
|
221
|
+
if message.get("role") == "assistant":
|
|
222
|
+
return message.get("content", "")
|
|
223
|
+
return ""
|
|
224
|
+
|
|
172
225
|
def _compose_lines(self, text: str, width: int) -> list[str]:
|
|
173
226
|
lines: list[str] = []
|
|
174
227
|
for paragraph in text.splitlines() or [""]:
|
|
@@ -278,7 +331,9 @@ class CobaltTUI:
|
|
|
278
331
|
for message_index, message in enumerate(self.history.latest(18)):
|
|
279
332
|
formatted_rows.extend((line, attr, message_index) for line, attr in self._format_message_lines(message, main_w - 8, message_index))
|
|
280
333
|
|
|
281
|
-
|
|
334
|
+
content_height = max(1, box_y - main_y - 2)
|
|
335
|
+
end = max(0, len(formatted_rows) - self.scroll_offset)
|
|
336
|
+
visible = formatted_rows[max(0, end - content_height):end]
|
|
282
337
|
for idx, (line, attr, _) in enumerate(visible[: main_h - 4]):
|
|
283
338
|
y = main_y + 1 + idx
|
|
284
339
|
if y >= box_y:
|
|
@@ -286,6 +341,10 @@ class CobaltTUI:
|
|
|
286
341
|
clipped = line[: max(0, main_w - 4)]
|
|
287
342
|
stdscr.addstr(y, main_x + 2, clipped, attr)
|
|
288
343
|
|
|
344
|
+
copy_label = "[ Copy response ]"
|
|
345
|
+
copy_y = min(box_y - 1, main_y + main_h - box_height - 2)
|
|
346
|
+
stdscr.addstr(copy_y, main_x + 2, copy_label, curses.A_DIM)
|
|
347
|
+
|
|
289
348
|
self._draw_input_box(stdscr, box_x, box_y, box_width, box_height, input_lines, self.input_focused)
|
|
290
349
|
|
|
291
350
|
if status:
|
|
@@ -340,6 +399,7 @@ class CobaltTUI:
|
|
|
340
399
|
self.selected_chat_path = str(target)
|
|
341
400
|
self.history = self.chat_store.load_chat(target)
|
|
342
401
|
self.current_chat = self.history
|
|
402
|
+
self.scroll_offset = 0
|
|
343
403
|
return True
|
|
344
404
|
return False
|
|
345
405
|
|
|
@@ -353,6 +413,34 @@ class CobaltTUI:
|
|
|
353
413
|
box_x, box_y, box_width, box_height, _ = self._input_box_geometry(main_x, main_y, main_w, main_h, input_text)
|
|
354
414
|
return box_x <= x < box_x + box_width and box_y <= y < box_y + box_height
|
|
355
415
|
|
|
416
|
+
def _copy_button_at(self, x: int, y: int, input_text: str) -> str | None:
|
|
417
|
+
height, width = curses.LINES, curses.COLS
|
|
418
|
+
sidebar_w = min(26, max(20, width // 4))
|
|
419
|
+
main_x, main_y = 3 + sidebar_w, 6
|
|
420
|
+
main_w, main_h = max(20, width - main_x - 2), max(8, height - 9)
|
|
421
|
+
_, box_y, box_width, box_height, _ = self._input_box_geometry(main_x, main_y, main_w, main_h, input_text)
|
|
422
|
+
prompt_copy_x = main_x + 2 + box_width - len("[ Copy prompt ]") - 2
|
|
423
|
+
if prompt_copy_x <= x < prompt_copy_x + len("[ Copy prompt ]") and box_y + 1 <= y <= box_y + 1:
|
|
424
|
+
return "prompt"
|
|
425
|
+
if self.history.messages:
|
|
426
|
+
copy_y = min(box_y - 1, main_y + main_h - box_height - 2)
|
|
427
|
+
if main_x + 2 <= x < main_x + 2 + len("[ Copy response ]") and y == copy_y:
|
|
428
|
+
return "response"
|
|
429
|
+
return None
|
|
430
|
+
|
|
431
|
+
def _paste_button_at(self, x: int, y: int, input_text: str) -> bool:
|
|
432
|
+
height, width = curses.LINES, curses.COLS
|
|
433
|
+
sidebar_w = min(26, max(20, width // 4))
|
|
434
|
+
main_x, main_y = 3 + sidebar_w, 6
|
|
435
|
+
main_w, main_h = max(20, width - main_x - 2), max(8, height - 9)
|
|
436
|
+
_, box_y, box_width, _, _ = self._input_box_geometry(main_x, main_y, main_w, main_h, input_text)
|
|
437
|
+
label = "[ Paste ]"
|
|
438
|
+
paste_x = main_x + 2 + box_width - len("[ Copy prompt ]") - len(label) - 4
|
|
439
|
+
return paste_x <= x < paste_x + len(label) and y == box_y + 1
|
|
440
|
+
|
|
441
|
+
def _paste_into_input(self, input_text: str) -> str:
|
|
442
|
+
return input_text + self._paste_from_clipboard()
|
|
443
|
+
|
|
356
444
|
def _handle_thinking_click(self, x: int, y: int) -> bool:
|
|
357
445
|
height, width = curses.LINES, curses.COLS
|
|
358
446
|
sidebar_w = min(26, max(20, width // 4))
|
|
@@ -394,7 +482,30 @@ class CobaltTUI:
|
|
|
394
482
|
ch = stdscr.getch()
|
|
395
483
|
|
|
396
484
|
if ch == curses.KEY_MOUSE:
|
|
397
|
-
|
|
485
|
+
try:
|
|
486
|
+
_, x, y, _, button_state = curses.getmouse()
|
|
487
|
+
except curses.error:
|
|
488
|
+
continue
|
|
489
|
+
if button_state & getattr(curses, "BUTTON4_PRESSED", 0):
|
|
490
|
+
self.scroll_offset = min(self.scroll_offset + 3, max(0, len(self.history.messages) * 3))
|
|
491
|
+
continue
|
|
492
|
+
if button_state & getattr(curses, "BUTTON5_PRESSED", 0):
|
|
493
|
+
self.scroll_offset = max(0, self.scroll_offset - 3)
|
|
494
|
+
continue
|
|
495
|
+
copy_target = self._copy_button_at(x, y, input_text) if button_state & (curses.BUTTON1_PRESSED | curses.BUTTON1_CLICKED | curses.BUTTON1_RELEASED) else None
|
|
496
|
+
if copy_target:
|
|
497
|
+
try:
|
|
498
|
+
self._copy_to_clipboard(self._latest_response() if copy_target == "response" else input_text)
|
|
499
|
+
except (OSError, RuntimeError, ValueError) as exc:
|
|
500
|
+
self.history.add("assistant", f"Error: {exc}")
|
|
501
|
+
continue
|
|
502
|
+
if button_state & (curses.BUTTON1_PRESSED | curses.BUTTON1_CLICKED | curses.BUTTON1_RELEASED) and self._paste_button_at(x, y, input_text):
|
|
503
|
+
try:
|
|
504
|
+
input_text += self._paste_from_clipboard()
|
|
505
|
+
self.input_focused = True
|
|
506
|
+
except (OSError, RuntimeError, ValueError) as exc:
|
|
507
|
+
self.history.add("assistant", f"Error: {exc}")
|
|
508
|
+
continue
|
|
398
509
|
button = self._handle_button_click(x, y, stdscr.getmaxyx()[1]) if button_state & (curses.BUTTON1_PRESSED | curses.BUTTON1_CLICKED | curses.BUTTON1_RELEASED) else None
|
|
399
510
|
if button in ("signin", "signup"):
|
|
400
511
|
self._open_auth(button)
|
|
@@ -429,6 +540,24 @@ class CobaltTUI:
|
|
|
429
540
|
continue
|
|
430
541
|
self.input_focused = False
|
|
431
542
|
continue
|
|
543
|
+
if ch == 22 and self.input_focused:
|
|
544
|
+
try:
|
|
545
|
+
input_text = self._paste_into_input(input_text)
|
|
546
|
+
except (OSError, RuntimeError, ValueError) as exc:
|
|
547
|
+
self.history.add("assistant", f"Error: {exc}")
|
|
548
|
+
continue
|
|
549
|
+
if ch == curses.KEY_UP:
|
|
550
|
+
self.scroll_offset = min(self.scroll_offset + 1, max(0, len(self.history.messages) * 3))
|
|
551
|
+
continue
|
|
552
|
+
if ch == curses.KEY_DOWN:
|
|
553
|
+
self.scroll_offset = max(0, self.scroll_offset - 1)
|
|
554
|
+
continue
|
|
555
|
+
if ch == curses.KEY_PPAGE:
|
|
556
|
+
self.scroll_offset = min(self.scroll_offset + 8, max(0, len(self.history.messages) * 3))
|
|
557
|
+
continue
|
|
558
|
+
if ch == curses.KEY_NPAGE:
|
|
559
|
+
self.scroll_offset = max(0, self.scroll_offset - 8)
|
|
560
|
+
continue
|
|
432
561
|
if self.auth_view:
|
|
433
562
|
if ch in (10, 13, curses.KEY_ENTER):
|
|
434
563
|
if self.auth_field == 0:
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import subprocess
|
|
1
2
|
from pathlib import Path
|
|
2
3
|
|
|
3
4
|
import pytest
|
|
@@ -60,6 +61,45 @@ def test_tui_buttons_replace_chat_shortcuts() -> None:
|
|
|
60
61
|
assert tui._handle_button_click(buttons["delete"][0], buttons["delete"][1], 120) == "delete"
|
|
61
62
|
|
|
62
63
|
|
|
64
|
+
def test_tui_copy_uses_available_clipboard_command(monkeypatch) -> None:
|
|
65
|
+
tui = CobaltTUI(settings=Settings())
|
|
66
|
+
tui.history.add("assistant", "response text")
|
|
67
|
+
captured = {}
|
|
68
|
+
|
|
69
|
+
monkeypatch.setattr("cobalt_cli_linux.tui.shutil.which", lambda command: "/usr/bin/wl-copy" if command == "wl-copy" else None)
|
|
70
|
+
monkeypatch.setattr("cobalt_cli_linux.tui.subprocess.run", lambda args, **kwargs: captured.update(args=args, kwargs=kwargs))
|
|
71
|
+
|
|
72
|
+
assert tui._latest_response() == "response text"
|
|
73
|
+
assert tui._copy_to_clipboard("prompt text") == "Copied"
|
|
74
|
+
assert captured["kwargs"]["input"] == "prompt text"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_tui_copy_falls_back_when_xsel_fails(monkeypatch) -> None:
|
|
78
|
+
tui = CobaltTUI(settings=Settings())
|
|
79
|
+
|
|
80
|
+
monkeypatch.setattr("cobalt_cli_linux.tui.shutil.which", lambda command: f"/usr/bin/{command}")
|
|
81
|
+
|
|
82
|
+
def run(args, **kwargs):
|
|
83
|
+
if not args[0].endswith("xsel"):
|
|
84
|
+
raise subprocess.CalledProcessError(1, args)
|
|
85
|
+
|
|
86
|
+
monkeypatch.setattr("cobalt_cli_linux.tui.subprocess.run", run)
|
|
87
|
+
|
|
88
|
+
assert tui._copy_to_clipboard("text") == "Copied"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_streaming_empty_answer_gets_visible_fallback(monkeypatch, tmp_path: Path) -> None:
|
|
92
|
+
agent = CobaltAgent(settings=Settings(), history=ConversationHistory(tmp_path / "history.json"))
|
|
93
|
+
agent.profile_store.data["active"] = "personal"
|
|
94
|
+
|
|
95
|
+
monkeypatch.setattr(agent.client, "chat_completion_stream_events", lambda *args, **kwargs: iter(()))
|
|
96
|
+
|
|
97
|
+
response = agent.process_stream("hello")
|
|
98
|
+
|
|
99
|
+
assert response.startswith("I could not produce a visible response")
|
|
100
|
+
assert agent.history.messages[-1]["content"] == response
|
|
101
|
+
|
|
102
|
+
|
|
63
103
|
def test_no_prompt_launches_tui(monkeypatch) -> None:
|
|
64
104
|
launched = []
|
|
65
105
|
|
|
@@ -222,3 +262,34 @@ def test_groq_client_auto_detects_available_model(monkeypatch) -> None:
|
|
|
222
262
|
client = GroqClient("test-key", model="auto")
|
|
223
263
|
assert client.resolve_model() == "llama-3.3-70b-versatile"
|
|
224
264
|
assert client.chat_completion([{"role": "user", "content": "hello"}]) == "ok"
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def test_groq_client_clamps_max_tokens_to_model_limit(monkeypatch) -> None:
|
|
268
|
+
captured = {}
|
|
269
|
+
|
|
270
|
+
class FakeResponse:
|
|
271
|
+
status_code = 200
|
|
272
|
+
|
|
273
|
+
def json(self):
|
|
274
|
+
return {"choices": [{"message": {"content": "ok"}}]}
|
|
275
|
+
|
|
276
|
+
class FakeClient:
|
|
277
|
+
def __init__(self, timeout=None):
|
|
278
|
+
pass
|
|
279
|
+
|
|
280
|
+
def __enter__(self):
|
|
281
|
+
return self
|
|
282
|
+
|
|
283
|
+
def __exit__(self, exc_type, exc, tb):
|
|
284
|
+
return False
|
|
285
|
+
|
|
286
|
+
def post(self, url, headers=None, json=None):
|
|
287
|
+
captured.update(json)
|
|
288
|
+
return FakeResponse()
|
|
289
|
+
|
|
290
|
+
monkeypatch.setattr("cobalt_cli_linux.groq_client.httpx.Client", FakeClient)
|
|
291
|
+
client = GroqClient("test-key", model="llama-3.1-8b-instant")
|
|
292
|
+
|
|
293
|
+
client.chat_completion([{"role": "user", "content": "hello"}], max_tokens=1024)
|
|
294
|
+
|
|
295
|
+
assert captured["max_tokens"] == 512
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/src/cobalt_cli_linux.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/src/cobalt_cli_linux.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/src/cobalt_cli_linux.egg-info/requires.txt
RENAMED
|
File without changes
|
{cobalt_cli_linux-0.1.5 → cobalt_cli_linux-0.1.7}/src/cobalt_cli_linux.egg-info/top_level.txt
RENAMED
|
File without changes
|