code-puppy 0.0.77__tar.gz → 0.0.79__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.
- {code_puppy-0.0.77 → code_puppy-0.0.79}/PKG-INFO +5 -3
- {code_puppy-0.0.77 → code_puppy-0.0.79}/README.md +4 -2
- {code_puppy-0.0.77 → code_puppy-0.0.79}/code_puppy/agent.py +2 -5
- code_puppy-0.0.79/code_puppy/command_line/motd.py +55 -0
- {code_puppy-0.0.77 → code_puppy-0.0.79}/code_puppy/main.py +51 -66
- code_puppy-0.0.79/code_puppy/message_history_processor.py +78 -0
- {code_puppy-0.0.77 → code_puppy-0.0.79}/code_puppy/models.json +12 -0
- code_puppy-0.0.79/code_puppy/state_management.py +42 -0
- {code_puppy-0.0.77 → code_puppy-0.0.79}/pyproject.toml +1 -1
- code_puppy-0.0.77/code_puppy/command_line/motd.py +0 -49
- {code_puppy-0.0.77 → code_puppy-0.0.79}/.gitignore +0 -0
- {code_puppy-0.0.77 → code_puppy-0.0.79}/LICENSE +0 -0
- {code_puppy-0.0.77 → code_puppy-0.0.79}/code_puppy/__init__.py +0 -0
- {code_puppy-0.0.77 → code_puppy-0.0.79}/code_puppy/agent_prompts.py +0 -0
- {code_puppy-0.0.77 → code_puppy-0.0.79}/code_puppy/command_line/__init__.py +0 -0
- {code_puppy-0.0.77 → code_puppy-0.0.79}/code_puppy/command_line/file_path_completion.py +0 -0
- {code_puppy-0.0.77 → code_puppy-0.0.79}/code_puppy/command_line/meta_command_handler.py +0 -0
- {code_puppy-0.0.77 → code_puppy-0.0.79}/code_puppy/command_line/model_picker_completion.py +0 -0
- {code_puppy-0.0.77 → code_puppy-0.0.79}/code_puppy/command_line/prompt_toolkit_completion.py +0 -0
- {code_puppy-0.0.77 → code_puppy-0.0.79}/code_puppy/command_line/utils.py +0 -0
- {code_puppy-0.0.77 → code_puppy-0.0.79}/code_puppy/config.py +0 -0
- {code_puppy-0.0.77 → code_puppy-0.0.79}/code_puppy/model_factory.py +0 -0
- {code_puppy-0.0.77 → code_puppy-0.0.79}/code_puppy/session_memory.py +0 -0
- {code_puppy-0.0.77 → code_puppy-0.0.79}/code_puppy/tools/__init__.py +0 -0
- {code_puppy-0.0.77 → code_puppy-0.0.79}/code_puppy/tools/command_runner.py +0 -0
- {code_puppy-0.0.77 → code_puppy-0.0.79}/code_puppy/tools/common.py +0 -0
- {code_puppy-0.0.77 → code_puppy-0.0.79}/code_puppy/tools/file_modifications.py +0 -0
- {code_puppy-0.0.77 → code_puppy-0.0.79}/code_puppy/tools/file_operations.py +0 -0
- {code_puppy-0.0.77 → code_puppy-0.0.79}/code_puppy/tools/ts_code_map.py +0 -0
- {code_puppy-0.0.77 → code_puppy-0.0.79}/code_puppy/version_checker.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code-puppy
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.79
|
|
4
4
|
Summary: Code generation agent
|
|
5
5
|
Author: Michael Pfaffenberger
|
|
6
6
|
License: MIT
|
|
@@ -67,8 +67,9 @@ Code Puppy is an AI-powered code generation agent, designed to understand progra
|
|
|
67
67
|
|
|
68
68
|
## Usage
|
|
69
69
|
```bash
|
|
70
|
-
export MODEL_NAME=gpt-
|
|
70
|
+
export MODEL_NAME=gpt-5 # or gemini-2.5-flash-preview-05-20 as an example for Google Gemini models
|
|
71
71
|
export OPENAI_API_KEY=<your_openai_api_key> # or GEMINI_API_KEY for Google Gemini models
|
|
72
|
+
export CEREBRAS_API_KEY=<your_cerebras_api_key> # for Cerebras models
|
|
72
73
|
export YOLO_MODE=true # to bypass the safety confirmation prompt when running shell commands
|
|
73
74
|
|
|
74
75
|
# or ...
|
|
@@ -106,7 +107,7 @@ export MODELS_JSON_PATH=/path/to/custom/models.json
|
|
|
106
107
|
}
|
|
107
108
|
}
|
|
108
109
|
```
|
|
109
|
-
Note that the `OPENAI_API_KEY` env variable must be set when using `custom_openai` endpoints.
|
|
110
|
+
Note that the `OPENAI_API_KEY` or `CEREBRAS_API_KEY` env variable must be set when using `custom_openai` endpoints.
|
|
110
111
|
|
|
111
112
|
Open an issue if your environment is somehow weirder than mine.
|
|
112
113
|
|
|
@@ -122,6 +123,7 @@ code-puppy "write me a C++ hello world program in /tmp/main.cpp then compile it
|
|
|
122
123
|
- Python 3.9+
|
|
123
124
|
- OpenAI API key (for GPT models)
|
|
124
125
|
- Gemini API key (for Google's Gemini models)
|
|
126
|
+
- Cerebras API key (for Cerebras models)
|
|
125
127
|
- Anthropic key (for Claude models)
|
|
126
128
|
- Ollama endpoint available
|
|
127
129
|
|
|
@@ -34,8 +34,9 @@ Code Puppy is an AI-powered code generation agent, designed to understand progra
|
|
|
34
34
|
|
|
35
35
|
## Usage
|
|
36
36
|
```bash
|
|
37
|
-
export MODEL_NAME=gpt-
|
|
37
|
+
export MODEL_NAME=gpt-5 # or gemini-2.5-flash-preview-05-20 as an example for Google Gemini models
|
|
38
38
|
export OPENAI_API_KEY=<your_openai_api_key> # or GEMINI_API_KEY for Google Gemini models
|
|
39
|
+
export CEREBRAS_API_KEY=<your_cerebras_api_key> # for Cerebras models
|
|
39
40
|
export YOLO_MODE=true # to bypass the safety confirmation prompt when running shell commands
|
|
40
41
|
|
|
41
42
|
# or ...
|
|
@@ -73,7 +74,7 @@ export MODELS_JSON_PATH=/path/to/custom/models.json
|
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
```
|
|
76
|
-
Note that the `OPENAI_API_KEY` env variable must be set when using `custom_openai` endpoints.
|
|
77
|
+
Note that the `OPENAI_API_KEY` or `CEREBRAS_API_KEY` env variable must be set when using `custom_openai` endpoints.
|
|
77
78
|
|
|
78
79
|
Open an issue if your environment is somehow weirder than mine.
|
|
79
80
|
|
|
@@ -89,6 +90,7 @@ code-puppy "write me a C++ hello world program in /tmp/main.cpp then compile it
|
|
|
89
90
|
- Python 3.9+
|
|
90
91
|
- OpenAI API key (for GPT models)
|
|
91
92
|
- Gemini API key (for Google's Gemini models)
|
|
93
|
+
- Cerebras API key (for Cerebras models)
|
|
92
94
|
- Anthropic key (for Claude models)
|
|
93
95
|
- Ollama endpoint available
|
|
94
96
|
|
|
@@ -8,6 +8,7 @@ from pydantic_ai.mcp import MCPServerSSE
|
|
|
8
8
|
from code_puppy.agent_prompts import get_system_prompt
|
|
9
9
|
from code_puppy.model_factory import ModelFactory
|
|
10
10
|
from code_puppy.session_memory import SessionMemory
|
|
11
|
+
from code_puppy.state_management import message_history_accumulator
|
|
11
12
|
from code_puppy.tools import register_all_tools
|
|
12
13
|
from code_puppy.tools.common import console
|
|
13
14
|
|
|
@@ -83,11 +84,6 @@ def reload_code_generation_agent():
|
|
|
83
84
|
global _code_generation_agent, _LAST_MODEL_NAME
|
|
84
85
|
from code_puppy.config import get_model_name
|
|
85
86
|
|
|
86
|
-
model_name = get_model_name()
|
|
87
|
-
console.print(f"[bold cyan]Loading Model: {model_name}")
|
|
88
|
-
global _code_generation_agent, _LAST_MODEL_NAME
|
|
89
|
-
from code_puppy.config import get_model_name
|
|
90
|
-
|
|
91
87
|
model_name = get_model_name()
|
|
92
88
|
console.print(f"[bold cyan]Loading Model: {model_name}[/bold cyan]")
|
|
93
89
|
models_path = (
|
|
@@ -105,6 +101,7 @@ def reload_code_generation_agent():
|
|
|
105
101
|
instructions=instructions,
|
|
106
102
|
output_type=str,
|
|
107
103
|
retries=3,
|
|
104
|
+
history_processors=[message_history_accumulator]
|
|
108
105
|
)
|
|
109
106
|
register_all_tools(agent)
|
|
110
107
|
_code_generation_agent = agent
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MOTD (Message of the Day) feature for code-puppy.
|
|
3
|
+
Stores seen versions in ~/.puppy_cfg/motd.txt.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
MOTD_VERSION = "20250815"
|
|
9
|
+
MOTD_MESSAGE = """
|
|
10
|
+
/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯\\
|
|
11
|
+
| 🐾 Happy Friday, Aug 15, 2025! |
|
|
12
|
+
| |
|
|
13
|
+
| Biscuit the code puppy is on full zoomie mode! |
|
|
14
|
+
| Major paws-ups: |
|
|
15
|
+
| 1. We now integrate Cerebras gpt-oss-120b! |
|
|
16
|
+
| It's a bit underwhelming compared to Qwen3-Coder-480b |
|
|
17
|
+
| (obviously), but it's still good for basic fetches. |
|
|
18
|
+
| 2. We also added support for OpenAI gpt-5! |
|
|
19
|
+
| It's so good, it'll make you want to teach it to sit!|
|
|
20
|
+
| |
|
|
21
|
+
| • To use one of the Cerebras models just have a |
|
|
22
|
+
| CEREBRAS_API_KEY set in the environment variables. |
|
|
23
|
+
| • Use ~m to swap models in the middle of your session! |
|
|
24
|
+
| • Take stretch breaks – you'll need 'em! |
|
|
25
|
+
| • DRY your code, but keep your pup hydrated. |
|
|
26
|
+
| • If you hit a bug, treat yourself for finding it! |
|
|
27
|
+
| |
|
|
28
|
+
| Today: sniff, code, roll over, and let these fancy AI |
|
|
29
|
+
| models do the heavy lifting. Fire up a ~motd anytime |
|
|
30
|
+
| you need some puppy hype! |
|
|
31
|
+
\___________________________________________________________/
|
|
32
|
+
"""
|
|
33
|
+
MOTD_TRACK_FILE = os.path.expanduser("~/.puppy_cfg/motd.txt")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def has_seen_motd(version: str) -> bool:
|
|
37
|
+
if not os.path.exists(MOTD_TRACK_FILE):
|
|
38
|
+
return False
|
|
39
|
+
with open(MOTD_TRACK_FILE, "r") as f:
|
|
40
|
+
seen_versions = {line.strip() for line in f if line.strip()}
|
|
41
|
+
return version in seen_versions
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def mark_motd_seen(version: str):
|
|
45
|
+
os.makedirs(os.path.dirname(MOTD_TRACK_FILE), exist_ok=True)
|
|
46
|
+
with open(MOTD_TRACK_FILE, "a") as f:
|
|
47
|
+
f.write(f"{version}\n")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def print_motd(console, force: bool = False) -> bool:
|
|
51
|
+
if force or not has_seen_motd(MOTD_VERSION):
|
|
52
|
+
console.print(MOTD_MESSAGE)
|
|
53
|
+
mark_motd_seen(MOTD_VERSION)
|
|
54
|
+
return True
|
|
55
|
+
return False
|
|
@@ -9,17 +9,19 @@ from rich.markdown import CodeBlock, Markdown
|
|
|
9
9
|
from rich.syntax import Syntax
|
|
10
10
|
from rich.text import Text
|
|
11
11
|
|
|
12
|
-
from code_puppy import __version__
|
|
12
|
+
from code_puppy import __version__, state_management
|
|
13
13
|
from code_puppy.agent import get_code_generation_agent, session_memory
|
|
14
14
|
from code_puppy.command_line.prompt_toolkit_completion import (
|
|
15
15
|
get_input_with_combined_completion,
|
|
16
16
|
get_prompt_with_active_model,
|
|
17
17
|
)
|
|
18
18
|
from code_puppy.config import ensure_config_exists
|
|
19
|
+
from code_puppy.state_management import get_message_history, set_message_history
|
|
19
20
|
|
|
20
21
|
# Initialize rich console for pretty output
|
|
21
22
|
from code_puppy.tools.common import console
|
|
22
23
|
from code_puppy.version_checker import fetch_latest_version
|
|
24
|
+
from code_puppy.message_history_processor import message_history_processor
|
|
23
25
|
|
|
24
26
|
# from code_puppy.tools import * # noqa: F403
|
|
25
27
|
|
|
@@ -130,8 +132,6 @@ async def interactive_mode(history_file_path: str) -> None:
|
|
|
130
132
|
"[yellow]Falling back to basic input without tab completion[/yellow]"
|
|
131
133
|
)
|
|
132
134
|
|
|
133
|
-
message_history = []
|
|
134
|
-
|
|
135
135
|
# Set up history file in home directory
|
|
136
136
|
history_file_path_prompt = os.path.expanduser("~/.code_puppy_history.txt")
|
|
137
137
|
history_dir = os.path.dirname(history_file_path_prompt)
|
|
@@ -172,7 +172,7 @@ async def interactive_mode(history_file_path: str) -> None:
|
|
|
172
172
|
|
|
173
173
|
# Check for clear command (supports both `clear` and `~clear`)
|
|
174
174
|
if task.strip().lower() in ("clear", "~clear"):
|
|
175
|
-
|
|
175
|
+
state_management._message_history = []
|
|
176
176
|
console.print("[bold yellow]Conversation history cleared![/bold yellow]")
|
|
177
177
|
console.print(
|
|
178
178
|
"[dim]The agent will not remember previous interactions.[/dim]\n"
|
|
@@ -192,71 +192,56 @@ async def interactive_mode(history_file_path: str) -> None:
|
|
|
192
192
|
|
|
193
193
|
try:
|
|
194
194
|
prettier_code_blocks()
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
if not
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
grouped, no_group = group_by_tool_call_id(message_history)
|
|
243
|
-
# Flatten into groups or singletons
|
|
244
|
-
grouped_msgs = list(grouped.values()) + [[m] for m in no_group]
|
|
245
|
-
# Flattened history (latest groups/singletons last, trunc to N messages total),
|
|
246
|
-
# but always keep complete tool_call_id groups together
|
|
247
|
-
truncated = []
|
|
248
|
-
count = 0
|
|
249
|
-
for group in reversed(grouped_msgs):
|
|
250
|
-
if count + len(group) > limit:
|
|
251
|
-
break
|
|
252
|
-
truncated[:0] = group # insert at front
|
|
253
|
-
count += len(group)
|
|
254
|
-
message_history = truncated
|
|
255
|
-
# --- END GROUP-AWARE TRUNCATION LOGIC ---
|
|
195
|
+
local_cancelled = False
|
|
196
|
+
async def run_agent_task():
|
|
197
|
+
try:
|
|
198
|
+
agent = get_code_generation_agent()
|
|
199
|
+
async with agent.run_mcp_servers():
|
|
200
|
+
return await agent.run(
|
|
201
|
+
task,
|
|
202
|
+
message_history=get_message_history()
|
|
203
|
+
)
|
|
204
|
+
except Exception as e:
|
|
205
|
+
console.log("Task failed", e)
|
|
206
|
+
|
|
207
|
+
agent_task = asyncio.create_task(run_agent_task())
|
|
208
|
+
|
|
209
|
+
import signal
|
|
210
|
+
|
|
211
|
+
original_handler = None
|
|
212
|
+
|
|
213
|
+
def keyboard_interrupt_handler(sig, frame):
|
|
214
|
+
nonlocal local_cancelled
|
|
215
|
+
if not agent_task.done():
|
|
216
|
+
set_message_history(
|
|
217
|
+
message_history_processor(
|
|
218
|
+
get_message_history()
|
|
219
|
+
)
|
|
220
|
+
)
|
|
221
|
+
agent_task.cancel()
|
|
222
|
+
local_cancelled = True
|
|
223
|
+
|
|
224
|
+
try:
|
|
225
|
+
original_handler = signal.getsignal(signal.SIGINT)
|
|
226
|
+
signal.signal(signal.SIGINT, keyboard_interrupt_handler)
|
|
227
|
+
result = await agent_task
|
|
228
|
+
except asyncio.CancelledError:
|
|
229
|
+
pass
|
|
230
|
+
finally:
|
|
231
|
+
if original_handler:
|
|
232
|
+
signal.signal(signal.SIGINT, original_handler)
|
|
233
|
+
|
|
234
|
+
if local_cancelled:
|
|
235
|
+
console.print("Task canceled by user")
|
|
236
|
+
else:
|
|
237
|
+
agent_response = result.output
|
|
238
|
+
console.print(agent_response)
|
|
239
|
+
filtered = message_history_processor(get_message_history())
|
|
240
|
+
set_message_history(filtered)
|
|
256
241
|
|
|
257
242
|
# Show context status
|
|
258
243
|
console.print(
|
|
259
|
-
f"[dim]Context: {len(
|
|
244
|
+
f"[dim]Context: {len(get_message_history())} messages in history[/dim]\n"
|
|
260
245
|
)
|
|
261
246
|
|
|
262
247
|
except Exception:
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import queue
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
from pydantic_ai.messages import ModelMessage, ToolCallPart, ToolReturnPart
|
|
5
|
+
|
|
6
|
+
from code_puppy.config import get_message_history_limit
|
|
7
|
+
from code_puppy.tools.common import console
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def message_history_processor(messages: List[ModelMessage]) -> List[ModelMessage]:
|
|
11
|
+
"""
|
|
12
|
+
Truncate message history to manage token usage while preserving context.
|
|
13
|
+
|
|
14
|
+
This implementation:
|
|
15
|
+
- Uses the configurable message_history_limit from puppy.cfg (defaults to 40)
|
|
16
|
+
- Preserves system messages at the beginning
|
|
17
|
+
- Maintains tool call/response pairs together
|
|
18
|
+
- Follows PydanticAI best practices for message ordering
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
messages: List of ModelMessage objects from conversation history
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Truncated list of ModelMessage objects
|
|
25
|
+
"""
|
|
26
|
+
if not messages:
|
|
27
|
+
return messages
|
|
28
|
+
|
|
29
|
+
# Get the configurable limit from puppy.cfg
|
|
30
|
+
max_messages = get_message_history_limit()
|
|
31
|
+
# If we have max_messages or fewer, no truncation needed
|
|
32
|
+
if len(messages) <= max_messages:
|
|
33
|
+
return messages
|
|
34
|
+
|
|
35
|
+
console.print(
|
|
36
|
+
f"Truncating message history to manage token usage: {max_messages}"
|
|
37
|
+
)
|
|
38
|
+
result = []
|
|
39
|
+
result.append(messages[0]) # this is the system prompt
|
|
40
|
+
remaining_messages_to_fill = max_messages - 1
|
|
41
|
+
stack = queue.LifoQueue()
|
|
42
|
+
count = 0
|
|
43
|
+
tool_call_parts = set()
|
|
44
|
+
tool_return_parts = set()
|
|
45
|
+
for message in reversed(messages):
|
|
46
|
+
stack.put(message)
|
|
47
|
+
count += 1
|
|
48
|
+
if count >= remaining_messages_to_fill:
|
|
49
|
+
break
|
|
50
|
+
|
|
51
|
+
while not stack.empty():
|
|
52
|
+
item = stack.get()
|
|
53
|
+
for part in item.parts:
|
|
54
|
+
if hasattr(part, "tool_call_id") and part.tool_call_id:
|
|
55
|
+
if isinstance(part, ToolCallPart):
|
|
56
|
+
tool_call_parts.add(part.tool_call_id)
|
|
57
|
+
if isinstance(part, ToolReturnPart):
|
|
58
|
+
tool_return_parts.add(part.tool_call_id)
|
|
59
|
+
|
|
60
|
+
result.append(item)
|
|
61
|
+
|
|
62
|
+
missmatched_tool_call_ids = (tool_call_parts.union(tool_return_parts)) - (
|
|
63
|
+
tool_call_parts.intersection(tool_return_parts)
|
|
64
|
+
)
|
|
65
|
+
# trust...
|
|
66
|
+
final_result = result
|
|
67
|
+
if missmatched_tool_call_ids:
|
|
68
|
+
final_result = []
|
|
69
|
+
for msg in result:
|
|
70
|
+
is_missmatched = False
|
|
71
|
+
for part in msg.parts:
|
|
72
|
+
if hasattr(part, "tool_call_id"):
|
|
73
|
+
if part.tool_call_id in missmatched_tool_call_ids:
|
|
74
|
+
is_missmatched = True
|
|
75
|
+
if is_missmatched:
|
|
76
|
+
continue
|
|
77
|
+
final_result.append(msg)
|
|
78
|
+
return final_result
|
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
"type": "openai",
|
|
12
12
|
"name": "gpt-4.1-mini"
|
|
13
13
|
},
|
|
14
|
+
"gpt-5": {
|
|
15
|
+
"type": "openai",
|
|
16
|
+
"name": "gpt-5"
|
|
17
|
+
},
|
|
14
18
|
"gpt-4.1-nano": {
|
|
15
19
|
"type": "openai",
|
|
16
20
|
"name": "gpt-4.1-nano"
|
|
@@ -88,6 +92,14 @@
|
|
|
88
92
|
"api_key": "$CEREBRAS_API_KEY"
|
|
89
93
|
}
|
|
90
94
|
},
|
|
95
|
+
"Cerebras-gpt-oss-120b": {
|
|
96
|
+
"type": "custom_openai",
|
|
97
|
+
"name": "gpt-oss-120b",
|
|
98
|
+
"custom_endpoint": {
|
|
99
|
+
"url": "https://api.cerebras.ai/v1",
|
|
100
|
+
"api_key": "$CEREBRAS_API_KEY"
|
|
101
|
+
}
|
|
102
|
+
},
|
|
91
103
|
"Cerebras-Qwen-3-32b": {
|
|
92
104
|
"type": "custom_openai",
|
|
93
105
|
"name": "qwen-3-32b",
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from typing import Any, List
|
|
2
|
+
|
|
3
|
+
from code_puppy.tools.common import console
|
|
4
|
+
|
|
5
|
+
_message_history: List[Any] = []
|
|
6
|
+
|
|
7
|
+
def get_message_history() -> List[Any]:
|
|
8
|
+
return _message_history
|
|
9
|
+
|
|
10
|
+
def set_message_history(history: List[Any]) -> None:
|
|
11
|
+
global _message_history
|
|
12
|
+
_message_history = history
|
|
13
|
+
|
|
14
|
+
def clear_message_history() -> None:
|
|
15
|
+
global _message_history
|
|
16
|
+
_message_history = []
|
|
17
|
+
|
|
18
|
+
def append_to_message_history(message: Any) -> None:
|
|
19
|
+
_message_history.append(message)
|
|
20
|
+
|
|
21
|
+
def extend_message_history(history: List[Any]) -> None:
|
|
22
|
+
_message_history.extend(history)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def hash_message(message):
|
|
26
|
+
hashable_entities = []
|
|
27
|
+
for part in message.parts:
|
|
28
|
+
if hasattr(part, "timestamp"):
|
|
29
|
+
hashable_entities.append(part.timestamp.isoformat())
|
|
30
|
+
elif hasattr(part, "tool_call_id"):
|
|
31
|
+
hashable_entities.append(part.tool_call_id)
|
|
32
|
+
else:
|
|
33
|
+
hashable_entities.append(part.content)
|
|
34
|
+
return hash(",".join(hashable_entities))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def message_history_accumulator(messages: List[Any]):
|
|
38
|
+
message_history_hashes = set([hash_message(m) for m in _message_history])
|
|
39
|
+
for msg in messages:
|
|
40
|
+
if hash_message(msg) not in message_history_hashes:
|
|
41
|
+
_message_history.append(msg)
|
|
42
|
+
return messages
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
MOTD (Message of the Day) feature for code-puppy.
|
|
3
|
-
Stores seen versions in ~/.puppy_cfg/motd.txt.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
import os
|
|
7
|
-
|
|
8
|
-
MOTD_VERSION = "20250802"
|
|
9
|
-
MOTD_MESSAGE = """
|
|
10
|
-
/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯\\
|
|
11
|
-
| 🐾 Happy Sat-urday, Aug 2, 2025! |
|
|
12
|
-
| |
|
|
13
|
-
| Biscuit the code puppy is on full zoomie mode! |
|
|
14
|
-
| Major paws-up: We now integrate Cerebras Qwen3 Coder |
|
|
15
|
-
| 480b! YES, that’s 480 billion parameters of tail-wagging|
|
|
16
|
-
| code speed. It’s so fast, even my fetch can’t keep up! |
|
|
17
|
-
| |
|
|
18
|
-
| • Take stretch breaks – you’ll need ‘em! |
|
|
19
|
-
| • DRY your code, but keep your pup hydrated. |
|
|
20
|
-
| • If you hit a bug, treat yourself for finding it! |
|
|
21
|
-
| |
|
|
22
|
-
| Today: sniff, code, roll over, and let Cerebras Qwen3 |
|
|
23
|
-
| Coder 480b do the heavy lifting. Fire up a ~motd anytime|
|
|
24
|
-
| you need some puppy hype! |
|
|
25
|
-
\___________________________________________________________/
|
|
26
|
-
"""
|
|
27
|
-
MOTD_TRACK_FILE = os.path.expanduser("~/.puppy_cfg/motd.txt")
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def has_seen_motd(version: str) -> bool:
|
|
31
|
-
if not os.path.exists(MOTD_TRACK_FILE):
|
|
32
|
-
return False
|
|
33
|
-
with open(MOTD_TRACK_FILE, "r") as f:
|
|
34
|
-
seen_versions = {line.strip() for line in f if line.strip()}
|
|
35
|
-
return version in seen_versions
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def mark_motd_seen(version: str):
|
|
39
|
-
os.makedirs(os.path.dirname(MOTD_TRACK_FILE), exist_ok=True)
|
|
40
|
-
with open(MOTD_TRACK_FILE, "a") as f:
|
|
41
|
-
f.write(f"{version}\n")
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def print_motd(console, force: bool = False) -> bool:
|
|
45
|
-
if force or not has_seen_motd(MOTD_VERSION):
|
|
46
|
-
console.print(MOTD_MESSAGE)
|
|
47
|
-
mark_motd_seen(MOTD_VERSION)
|
|
48
|
-
return True
|
|
49
|
-
return False
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{code_puppy-0.0.77 → code_puppy-0.0.79}/code_puppy/command_line/prompt_toolkit_completion.py
RENAMED
|
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
|