opalacoder 0.1.19__tar.gz → 0.1.22__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.
- {opalacoder-0.1.19 → opalacoder-0.1.22}/PKG-INFO +1 -1
- opalacoder-0.1.22/docs/specs/backlog_register.md +39 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/gui_src/src/App.jsx +306 -16
- {opalacoder-0.1.19 → opalacoder-0.1.22}/gui_src/src/index.css +7 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/agent_stdin.py +15 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/cli_commands.py +52 -1
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/config.py +12 -3
- opalacoder-0.1.19/opalacoder/gui/assets/index-Cka3h8dT.js → opalacoder-0.1.22/opalacoder/gui/assets/index-BCdp1z3q.js +53 -53
- opalacoder-0.1.19/opalacoder/gui/assets/index-BzHZtmXE.css → opalacoder-0.1.22/opalacoder/gui/assets/index-fY3LKaOS.css +1 -1
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/gui/index.html +2 -2
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/ide_server.py +95 -4
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/project.py +35 -10
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/skills.py +1 -1
- {opalacoder-0.1.19 → opalacoder-0.1.22}/pyproject.toml +1 -1
- opalacoder-0.1.22/skills/view-editor/SKILL.md +17 -0
- opalacoder-0.1.22/skills/view-editor/scripts/run_view_editor.py +52 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/tests/test_model_commands.py +25 -0
- opalacoder-0.1.22/tests/test_view_editor_skill.py +37 -0
- opalacoder-0.1.19/docs/specs/backlog_register.md +0 -28
- {opalacoder-0.1.19 → opalacoder-0.1.22}/.claude/rules/RULES.md +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/.claude/settings.json +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/.github/workflows/publish.yml +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/.gitignore +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/AGENTS.md +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/CLAUDE.md +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/GEMINI.md +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/README.md +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/agents.yaml +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/config.yaml +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/debug/scratch_tail.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/docs/guide/analysis_results.md +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/docs/guide/figs/logotipo.png +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/docs/guide/paper_memplan.md +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/docs/guide/skills-plugin-system.md +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/docs/specs/01-arquitetura.md +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/docs/specs/02-memgpt-orquestrador.md +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/docs/specs/03-skill-implement-feature.md +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/docs/specs/04-memoria.md +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/docs/specs/05-vcs-sombra.md +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/docs/specs/06-skills-e-plugins.md +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/docs/specs/07-configuracao.md +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/docs/specs/08-ide.md +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/docs/specs/09-ide-projectcreationinterface.md +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/docs/specs/README.md +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/gui_src/index.html +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/gui_src/package-lock.json +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/gui_src/package.json +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/gui_src/src/main.jsx +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/gui_src/vite.config.js +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/main.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/__init__.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/agents.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/api_keys.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/archival.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/cli.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/code_index.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/copy_gui.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/embeddings.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/i18n.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/memgpt_runtime.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/orchestrator.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/planner.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/plugins/__init__.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/plugins/html_css_js_tools.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/session.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/structured.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/terminal.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/terminal_manager.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/tools.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/vcs.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/vector_index.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/workflow_orchestrator.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/opalacoder/workflow_tools.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/pytest.ini +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/requirements.txt +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/skills/chat-orchestrator/SKILL.md +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/skills/command-line/SKILL.md +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/skills/command-line/scripts/command_executor.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/skills/skills_store/html-css-js/SKILL.md +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/skills/skills_store/html-css-js/scripts/check_contracts.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/skills/skills_store/implement-feature/SKILL.md +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/skills/skills_store/implement-feature/scripts/run_workflow.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/tests/test_agent_config.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/tests/test_agent_stdin.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/tests/test_code_index_integration.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/tests/test_command_line_skill.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/tests/test_complexity_evaluator.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/tests/test_context_guard.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/tests/test_file_delete.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/tests/test_i18n_coverage.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/tests/test_implement_feature_script.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/tests/test_memgpt_runtime.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/tests/test_planner_oracle_live.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/tests/test_planner_output.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/tests/test_project_store.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/tests/test_refinement_loop.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/tests/test_search_bugs.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/tests/test_skills_directory_loader.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/tests/test_unhashable_dict_bug.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/tests/test_verification_strategies.py +0 -0
- {opalacoder-0.1.19 → opalacoder-0.1.22}/tests/test_workflow_pipeline.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opalacoder
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.22
|
|
4
4
|
Summary: Autonomous coding agent with interactive planning and modular execution
|
|
5
5
|
Project-URL: Homepage, https://github.com/gilzamir/OpalaCoder
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/gilzamir/OpalaCoder/issues
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# BUGS TO FIX DETECTED ON LAST PUBLIC RELEASE (READ THE LAST SUBTOPIC)
|
|
2
|
+
|
|
3
|
+
## FROM CURRENT = 0.1.17 TO NEXT = 0.1.18
|
|
4
|
+
|
|
5
|
+
1. Terminal dont work, no obvious message in back terminal, but on IDE terminal we get the message "[OpalaCoder] Conexão com o terminal perdida. Reconectando...".
|
|
6
|
+
|
|
7
|
+
2. A janela de criação de projeto somente deveria permitir confirmar criação de projeto se o diretório especificado for válido.
|
|
8
|
+
|
|
9
|
+
3. New feature: install optional modules must be on IDE startup.
|
|
10
|
+
|
|
11
|
+
## FROM CURRENT = 0.1.18 TO NEXT = 0.1.19
|
|
12
|
+
|
|
13
|
+
1. Projeto não mostra nenhum erro quando o backend de modelos falha, por exemplo, tenta-se rodar um modelo que o ollama não tem instalado.
|
|
14
|
+
|
|
15
|
+
2. Ao criar um novo arquivo, ficou congelado em carregando.
|
|
16
|
+
|
|
17
|
+
3. A aba de Problems nunca mostra nada errado, mesmo tendo.
|
|
18
|
+
|
|
19
|
+
4. Adicionar opção de renomear arquivo/diretório selecionado
|
|
20
|
+
|
|
21
|
+
5. Adicionar opção de limpar output e problems.
|
|
22
|
+
|
|
23
|
+
6. Abrir mais de uma aba no editor de arquivo (vários arquivos abertos ao mesmo tempo)
|
|
24
|
+
|
|
25
|
+
7. Verificar se o agente de comunicação com o backend é um LLMAgentBlock e, se for, como está as configurações de limite de chamada de ferramentas e de reflexão e outras.
|
|
26
|
+
|
|
27
|
+
## FROM CURRENT = 0.1.19 TO NEXT = 0.1.20
|
|
28
|
+
|
|
29
|
+
1. Implementar a visualização do pensamento do agente em uma aba thinking do painel inferior Implementar em uma aba separada thinking
|
|
30
|
+
2. Aumentar tamanho da fonte quando digitar ctrl+ no editor, ou diminuir quando digitar ctrl-.
|
|
31
|
+
3. Disponibilizar uma skill da ide que permite ao chat visualizar o conteúdo atual do editor e retornar o trecho selecionado.
|
|
32
|
+
4. Colocar botão de interroper o agente.
|
|
33
|
+
5. Descobrir porque o agente granite4:latest demora a responder.
|
|
34
|
+
6. O erro ao criar um projeto em um diretório existente ou proibido deveria ser mostrado como mensagem na janela de criação de projeto e não no terminal (colocar mensagem correta, de acordo com exceção).
|
|
35
|
+
6.1 Se diretório já existe e der erro de permissão, mostrar erro de permissão, se diretório não existe, criar diretório com nome do projeto.
|
|
36
|
+
7. Colocar hint de completação de servidor ollama (já trazer preenchido com o valor que geralmente é).
|
|
37
|
+
8. Criar uma janela de configuração de modelo com parâmetros mais usados que são aceitos no agenticblocks. E criar um comando set-model-param param-name value que permite qualquer parâmetro geralmente permitido pro litellm/ollama. Cuidado para implementar controle de verdade (valores adequados de parâmetro, por example).
|
|
38
|
+
|
|
39
|
+
9. Permitir que mensagens no chat não podem ser copiadas.
|
|
@@ -68,6 +68,9 @@ export default function App() {
|
|
|
68
68
|
const [activeSidebarTab, setActiveSidebarTab] = useState('explorer');
|
|
69
69
|
const [activeBottomTab, setActiveBottomTab] = useState('output');
|
|
70
70
|
const [problems, setProblems] = useState([]);
|
|
71
|
+
const [thinkingLogs, setThinkingLogs] = useState([]);
|
|
72
|
+
const [showAdvancedParams, setShowAdvancedParams] = useState(false);
|
|
73
|
+
const [newProjError, setNewProjError] = useState('');
|
|
71
74
|
|
|
72
75
|
// IDE Settings States
|
|
73
76
|
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
|
|
@@ -111,6 +114,12 @@ export default function App() {
|
|
|
111
114
|
// Shape: { name, project_name, project_path, model, alternative_model, mode, description } | null
|
|
112
115
|
const [editingProject, setEditingProject] = useState(null);
|
|
113
116
|
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
if (!editingProject) {
|
|
119
|
+
setShowAdvancedParams(false);
|
|
120
|
+
}
|
|
121
|
+
}, [editingProject]);
|
|
122
|
+
|
|
114
123
|
const fetchProblems = async () => {
|
|
115
124
|
if (!activeProject) return;
|
|
116
125
|
try {
|
|
@@ -165,10 +174,21 @@ export default function App() {
|
|
|
165
174
|
const [newProjModel, setNewProjModel] = useState('gemini/gemini-2.5-flash');
|
|
166
175
|
const [newProjMode, setNewProjMode] = useState('auto');
|
|
167
176
|
const [newProjApiKey, setNewProjApiKey] = useState('');
|
|
168
|
-
const [newProjApiBase, setNewProjApiBase] = useState('');
|
|
177
|
+
const [newProjApiBase, setNewProjApiBase] = useState('http://localhost:11434/v1');
|
|
169
178
|
|
|
170
179
|
const chatEndRef = useRef(null);
|
|
171
180
|
const logEndRef = useRef(null);
|
|
181
|
+
const editorRef = useRef(null);
|
|
182
|
+
|
|
183
|
+
const handleEditorDidMount = (editor, monaco) => {
|
|
184
|
+
editorRef.current = editor;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
useEffect(() => {
|
|
188
|
+
if (!showNewProjectModal) {
|
|
189
|
+
setNewProjError('');
|
|
190
|
+
}
|
|
191
|
+
}, [showNewProjectModal]);
|
|
172
192
|
|
|
173
193
|
// Initial load
|
|
174
194
|
useEffect(() => {
|
|
@@ -234,12 +254,26 @@ export default function App() {
|
|
|
234
254
|
logEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
235
255
|
}, [terminalLogs]);
|
|
236
256
|
|
|
237
|
-
// Keybindings (Ctrl+S to save)
|
|
257
|
+
// Keybindings (Ctrl+S to save, Ctrl+ / Ctrl- to zoom)
|
|
238
258
|
useEffect(() => {
|
|
239
259
|
const handleKeyDown = (e) => {
|
|
240
260
|
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
|
|
241
261
|
e.preventDefault();
|
|
242
262
|
saveFile();
|
|
263
|
+
} else if ((e.ctrlKey || e.metaKey) && (e.key === '+' || e.key === '=')) {
|
|
264
|
+
e.preventDefault();
|
|
265
|
+
setEditorFontSize(prev => {
|
|
266
|
+
const nextVal = Math.min(30, prev + 1);
|
|
267
|
+
safeSetLocalStorage('editorFontSize', nextVal);
|
|
268
|
+
return nextVal;
|
|
269
|
+
});
|
|
270
|
+
} else if ((e.ctrlKey || e.metaKey) && e.key === '-') {
|
|
271
|
+
e.preventDefault();
|
|
272
|
+
setEditorFontSize(prev => {
|
|
273
|
+
const nextVal = Math.max(10, prev - 1);
|
|
274
|
+
safeSetLocalStorage('editorFontSize', nextVal);
|
|
275
|
+
return nextVal;
|
|
276
|
+
});
|
|
243
277
|
}
|
|
244
278
|
};
|
|
245
279
|
window.addEventListener('keydown', handleKeyDown);
|
|
@@ -802,6 +836,7 @@ export default function App() {
|
|
|
802
836
|
const handleCreateProject = async (e) => {
|
|
803
837
|
e.preventDefault();
|
|
804
838
|
if (!newProjName || !newProjPath) return;
|
|
839
|
+
setNewProjError('');
|
|
805
840
|
try {
|
|
806
841
|
const res = await fetch('/api/opalacoder/create-project', {
|
|
807
842
|
method: 'POST',
|
|
@@ -823,13 +858,15 @@ export default function App() {
|
|
|
823
858
|
setNewProjPath('');
|
|
824
859
|
setNewProjDesc('');
|
|
825
860
|
setNewProjApiKey('');
|
|
826
|
-
setNewProjApiBase('');
|
|
861
|
+
setNewProjApiBase('http://localhost:11434/v1');
|
|
827
862
|
fetchProjects();
|
|
828
863
|
} else {
|
|
829
864
|
const err = await res.json();
|
|
865
|
+
setNewProjError(err.error || 'Erro ao criar projeto.');
|
|
830
866
|
addLog('error', `Erro ao criar projeto: ${err.error}`);
|
|
831
867
|
}
|
|
832
868
|
} catch (err) {
|
|
869
|
+
setNewProjError(err.message || 'Erro ao criar projeto.');
|
|
833
870
|
addLog('error', `Erro ao criar: ${err.message}`);
|
|
834
871
|
}
|
|
835
872
|
};
|
|
@@ -864,6 +901,7 @@ export default function App() {
|
|
|
864
901
|
alternative_model: proj.alternative_model || '',
|
|
865
902
|
mode: proj.mode || 'auto',
|
|
866
903
|
description: proj.description || '',
|
|
904
|
+
model_params: proj.model_params || {},
|
|
867
905
|
});
|
|
868
906
|
};
|
|
869
907
|
|
|
@@ -882,6 +920,7 @@ export default function App() {
|
|
|
882
920
|
alternative_model: editingProject.alternative_model,
|
|
883
921
|
mode: editingProject.mode,
|
|
884
922
|
description: editingProject.description,
|
|
923
|
+
model_params: editingProject.model_params,
|
|
885
924
|
})
|
|
886
925
|
});
|
|
887
926
|
if (res.ok) {
|
|
@@ -914,6 +953,21 @@ export default function App() {
|
|
|
914
953
|
};
|
|
915
954
|
|
|
916
955
|
|
|
956
|
+
const handleInterruptAgent = async () => {
|
|
957
|
+
try {
|
|
958
|
+
const res = await fetch('/api/opalacoder/interrupt', {
|
|
959
|
+
method: 'POST',
|
|
960
|
+
});
|
|
961
|
+
if (res.ok) {
|
|
962
|
+
addLog('info', 'Sinal de interrupção enviado ao agente.');
|
|
963
|
+
} else {
|
|
964
|
+
addLog('error', 'Falha ao enviar sinal de interrupção.');
|
|
965
|
+
}
|
|
966
|
+
} catch (err) {
|
|
967
|
+
addLog('error', `Erro ao interromper: ${err.message}`);
|
|
968
|
+
}
|
|
969
|
+
};
|
|
970
|
+
|
|
917
971
|
const handleSendMessage = async (e) => {
|
|
918
972
|
if (e) e.preventDefault();
|
|
919
973
|
if (!chatInput.trim() || !activeProject || isAgentRunning) return;
|
|
@@ -923,8 +977,22 @@ export default function App() {
|
|
|
923
977
|
setChatMessages(prev => [...prev, { role: 'user', content: userText }]);
|
|
924
978
|
setIsAgentRunning(true);
|
|
925
979
|
setProblems([]);
|
|
980
|
+
setThinkingLogs([]);
|
|
926
981
|
addLog('info', `Iniciando agente para prompt: "${userText}"`);
|
|
927
982
|
|
|
983
|
+
let selectedText = '';
|
|
984
|
+
if (editorRef.current) {
|
|
985
|
+
try {
|
|
986
|
+
const model = editorRef.current.getModel();
|
|
987
|
+
const selection = editorRef.current.getSelection();
|
|
988
|
+
if (model && selection) {
|
|
989
|
+
selectedText = model.getValueInRange(selection);
|
|
990
|
+
}
|
|
991
|
+
} catch (e) {
|
|
992
|
+
// ignore
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
|
|
928
996
|
try {
|
|
929
997
|
const res = await fetch('/api/opalacoder/run', {
|
|
930
998
|
method: 'POST',
|
|
@@ -935,7 +1003,10 @@ export default function App() {
|
|
|
935
1003
|
prompt: userText,
|
|
936
1004
|
project_name: activeProject.name,
|
|
937
1005
|
project_path: activeProject.project_path,
|
|
938
|
-
model: activeProject.model
|
|
1006
|
+
model: activeProject.model,
|
|
1007
|
+
current_file: selectedFile || '',
|
|
1008
|
+
editor_content: fileContent || '',
|
|
1009
|
+
selected_text: selectedText || '',
|
|
939
1010
|
})
|
|
940
1011
|
});
|
|
941
1012
|
|
|
@@ -998,6 +1069,11 @@ export default function App() {
|
|
|
998
1069
|
break;
|
|
999
1070
|
case 'thought':
|
|
1000
1071
|
addLog('thought', data.content);
|
|
1072
|
+
setThinkingLogs(prev => [...prev, { timestamp: new Date().toLocaleTimeString(), content: data.content }]);
|
|
1073
|
+
break;
|
|
1074
|
+
case 'cancelled':
|
|
1075
|
+
addLog('warning', data.message || 'Execução cancelada.');
|
|
1076
|
+
setChatMessages(prev => [...prev, { role: 'assistant', content: `⚠️ Interrompido: ${data.message || 'A execução do agente foi parada.'}` }]);
|
|
1001
1077
|
break;
|
|
1002
1078
|
case 'tool_call':
|
|
1003
1079
|
addLog('tool_call', `Chamando: ${data.tool} (${JSON.stringify(data.arguments)})`);
|
|
@@ -1382,6 +1458,7 @@ export default function App() {
|
|
|
1382
1458
|
theme={theme === 'light' ? 'light' : 'vs-dark'}
|
|
1383
1459
|
value={fileContent}
|
|
1384
1460
|
onChange={(val) => setFileContent(val)}
|
|
1461
|
+
onMount={handleEditorDidMount}
|
|
1385
1462
|
options={{
|
|
1386
1463
|
minimap: { enabled: true },
|
|
1387
1464
|
fontSize: editorFontSize,
|
|
@@ -1427,6 +1504,12 @@ export default function App() {
|
|
|
1427
1504
|
>
|
|
1428
1505
|
PROBLEMS {problems.length > 0 && <span style={{ marginLeft: '4px', background: '#f48771', color: '#1e1e1e', borderRadius: '10px', padding: '0 6px', fontSize: '10px', fontWeight: 'bold' }}>{problems.length}</span>}
|
|
1429
1506
|
</span>
|
|
1507
|
+
<span
|
|
1508
|
+
className={`vscode-bottom-tab ${activeBottomTab === 'thinking' ? 'active' : ''}`}
|
|
1509
|
+
onClick={() => selectTab('thinking')}
|
|
1510
|
+
>
|
|
1511
|
+
THINKING
|
|
1512
|
+
</span>
|
|
1430
1513
|
<span
|
|
1431
1514
|
className={`vscode-bottom-tab ${activeBottomTab === 'terminal' ? 'active' : ''}`}
|
|
1432
1515
|
onClick={() => selectTab('terminal')}
|
|
@@ -1435,11 +1518,23 @@ export default function App() {
|
|
|
1435
1518
|
</span>
|
|
1436
1519
|
</div>
|
|
1437
1520
|
<div className="flex items-center" style={{ gap: '8px' }}>
|
|
1438
|
-
{(activeBottomTab === 'output' || activeBottomTab === 'problems') && (
|
|
1521
|
+
{(activeBottomTab === 'output' || activeBottomTab === 'problems' || activeBottomTab === 'thinking') && (
|
|
1439
1522
|
<button
|
|
1440
|
-
onClick={
|
|
1523
|
+
onClick={
|
|
1524
|
+
activeBottomTab === 'output'
|
|
1525
|
+
? () => setTerminalLogs([])
|
|
1526
|
+
: activeBottomTab === 'problems'
|
|
1527
|
+
? () => setProblems([])
|
|
1528
|
+
: () => setThinkingLogs([])
|
|
1529
|
+
}
|
|
1441
1530
|
className="vscode-bottom-panel-clear-btn"
|
|
1442
|
-
title={
|
|
1531
|
+
title={
|
|
1532
|
+
activeBottomTab === 'output'
|
|
1533
|
+
? 'Limpar Output'
|
|
1534
|
+
: activeBottomTab === 'problems'
|
|
1535
|
+
? 'Limpar Problemas'
|
|
1536
|
+
: 'Limpar Pensamentos'
|
|
1537
|
+
}
|
|
1443
1538
|
>
|
|
1444
1539
|
<Trash size={12} />
|
|
1445
1540
|
<span>Clear</span>
|
|
@@ -1506,6 +1601,22 @@ export default function App() {
|
|
|
1506
1601
|
</div>
|
|
1507
1602
|
)}
|
|
1508
1603
|
|
|
1604
|
+
{activeBottomTab === 'thinking' && (
|
|
1605
|
+
<div className="vscode-logs" style={{ height: '100%' }}>
|
|
1606
|
+
{thinkingLogs.length === 0 ? (
|
|
1607
|
+
<div style={{ color: '#808080', fontStyle: 'italic' }}>Nenhum pensamento gerado ainda. Execute uma instrução para ver o raciocínio do agente...</div>
|
|
1608
|
+
) : (
|
|
1609
|
+
thinkingLogs.map((log, i) => (
|
|
1610
|
+
<div key={i} className="text-[#da70d6]" style={{ display: 'flex', alignItems: 'flex-start', gap: '6px', marginBottom: '4px' }}>
|
|
1611
|
+
<span style={{ color: '#5a5a5a' }}>[{log.timestamp}]</span>
|
|
1612
|
+
<span style={{ fontWeight: 'bold' }}>[THOUGHT]</span>
|
|
1613
|
+
<span style={{ whiteSpace: 'pre-wrap', flex: 1, fontFamily: 'Consolas, monospace' }}>{log.content}</span>
|
|
1614
|
+
</div>
|
|
1615
|
+
))
|
|
1616
|
+
)}
|
|
1617
|
+
</div>
|
|
1618
|
+
)}
|
|
1619
|
+
|
|
1509
1620
|
<div style={{ display: activeBottomTab === 'terminal' ? 'block' : 'none', height: '100%', background: '#1e1e1e', overflow: 'hidden' }}>
|
|
1510
1621
|
{!activeProject ? (
|
|
1511
1622
|
<div style={{ color: '#808080', fontStyle: 'italic', padding: '16px' }}>Defina um projeto/workspace para habilitar o terminal.</div>
|
|
@@ -1604,14 +1715,26 @@ export default function App() {
|
|
|
1604
1715
|
placeholder={!activeProject ? "Defina um projeto..." : isAgentRunning ? "Pensando..." : "Pergunte ao OpalaCoder..."}
|
|
1605
1716
|
style={{ flex: 1 }}
|
|
1606
1717
|
/>
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1718
|
+
{isAgentRunning ? (
|
|
1719
|
+
<button
|
|
1720
|
+
type="button"
|
|
1721
|
+
onClick={handleInterruptAgent}
|
|
1722
|
+
className="vscode-button"
|
|
1723
|
+
style={{ padding: '6px', backgroundColor: '#f48771', color: '#1e1e1e' }}
|
|
1724
|
+
title="Interromper agente"
|
|
1725
|
+
>
|
|
1726
|
+
<X size={14} />
|
|
1727
|
+
</button>
|
|
1728
|
+
) : (
|
|
1729
|
+
<button
|
|
1730
|
+
type="submit"
|
|
1731
|
+
disabled={!activeProject || !chatInput.trim()}
|
|
1732
|
+
className="vscode-button"
|
|
1733
|
+
style={{ padding: '6px' }}
|
|
1734
|
+
>
|
|
1735
|
+
<ArrowRight size={14} />
|
|
1736
|
+
</button>
|
|
1737
|
+
)}
|
|
1615
1738
|
</div>
|
|
1616
1739
|
</form>
|
|
1617
1740
|
</aside>
|
|
@@ -1792,6 +1915,12 @@ export default function App() {
|
|
|
1792
1915
|
</div>
|
|
1793
1916
|
</div>
|
|
1794
1917
|
|
|
1918
|
+
{newProjError && (
|
|
1919
|
+
<div style={{ color: '#f48771', fontSize: '11px', marginTop: '4px', whiteSpace: 'pre-wrap' }}>
|
|
1920
|
+
⚠️ {newProjError}
|
|
1921
|
+
</div>
|
|
1922
|
+
)}
|
|
1923
|
+
|
|
1795
1924
|
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: '8px', paddingTop: '12px', borderTop: '1px solid #3c3c3c', marginTop: '4px' }}>
|
|
1796
1925
|
<button
|
|
1797
1926
|
type="button"
|
|
@@ -1961,6 +2090,167 @@ export default function App() {
|
|
|
1961
2090
|
/>
|
|
1962
2091
|
</div>
|
|
1963
2092
|
|
|
2093
|
+
{/* Collapsible advanced parameters */}
|
|
2094
|
+
<div className="flex flex-col" style={{ marginTop: '4px' }}>
|
|
2095
|
+
<button
|
|
2096
|
+
type="button"
|
|
2097
|
+
onClick={() => setShowAdvancedParams(!showAdvancedParams)}
|
|
2098
|
+
style={{
|
|
2099
|
+
background: 'transparent',
|
|
2100
|
+
border: 'none',
|
|
2101
|
+
color: '#007acc',
|
|
2102
|
+
cursor: 'pointer',
|
|
2103
|
+
display: 'flex',
|
|
2104
|
+
alignItems: 'center',
|
|
2105
|
+
gap: '4px',
|
|
2106
|
+
padding: '4px 0',
|
|
2107
|
+
fontSize: '12px',
|
|
2108
|
+
fontWeight: 'bold',
|
|
2109
|
+
textAlign: 'left',
|
|
2110
|
+
width: 'fit-content'
|
|
2111
|
+
}}
|
|
2112
|
+
>
|
|
2113
|
+
<span>{showAdvancedParams ? '▼' : '▶'} Parâmetros do Modelo (Avançado)</span>
|
|
2114
|
+
</button>
|
|
2115
|
+
|
|
2116
|
+
{showAdvancedParams && (
|
|
2117
|
+
<div
|
|
2118
|
+
style={{
|
|
2119
|
+
border: '1px solid #3c3c3c',
|
|
2120
|
+
borderRadius: '4px',
|
|
2121
|
+
padding: '12px',
|
|
2122
|
+
marginTop: '8px',
|
|
2123
|
+
backgroundColor: '#252526',
|
|
2124
|
+
display: 'grid',
|
|
2125
|
+
gridTemplateColumns: '1fr 1fr',
|
|
2126
|
+
gap: '12px'
|
|
2127
|
+
}}
|
|
2128
|
+
>
|
|
2129
|
+
<div className="flex flex-col" style={{ gap: '4px' }}>
|
|
2130
|
+
<label className="vscode-sidebar-section-title" style={{ padding: 0 }}>Temperature</label>
|
|
2131
|
+
<input
|
|
2132
|
+
type="number"
|
|
2133
|
+
step="0.1"
|
|
2134
|
+
min="0"
|
|
2135
|
+
max="2"
|
|
2136
|
+
placeholder="Ex: 0.7"
|
|
2137
|
+
value={editingProject.model_params?.temperature !== undefined ? editingProject.model_params.temperature : ''}
|
|
2138
|
+
onChange={e => {
|
|
2139
|
+
const val = e.target.value === '' ? undefined : parseFloat(e.target.value);
|
|
2140
|
+
setEditingProject(p => {
|
|
2141
|
+
const nextParams = { ...p.model_params };
|
|
2142
|
+
if (val === undefined) delete nextParams.temperature;
|
|
2143
|
+
else nextParams.temperature = val;
|
|
2144
|
+
return { ...p, model_params: nextParams };
|
|
2145
|
+
});
|
|
2146
|
+
}}
|
|
2147
|
+
/>
|
|
2148
|
+
</div>
|
|
2149
|
+
|
|
2150
|
+
<div className="flex flex-col" style={{ gap: '4px' }}>
|
|
2151
|
+
<label className="vscode-sidebar-section-title" style={{ padding: 0 }}>Max Tokens</label>
|
|
2152
|
+
<input
|
|
2153
|
+
type="number"
|
|
2154
|
+
min="1"
|
|
2155
|
+
placeholder="Ex: 4096"
|
|
2156
|
+
value={editingProject.model_params?.max_tokens !== undefined ? editingProject.model_params.max_tokens : ''}
|
|
2157
|
+
onChange={e => {
|
|
2158
|
+
const val = e.target.value === '' ? undefined : parseInt(e.target.value, 10);
|
|
2159
|
+
setEditingProject(p => {
|
|
2160
|
+
const nextParams = { ...p.model_params };
|
|
2161
|
+
if (val === undefined) delete nextParams.max_tokens;
|
|
2162
|
+
else nextParams.max_tokens = val;
|
|
2163
|
+
return { ...p, model_params: nextParams };
|
|
2164
|
+
});
|
|
2165
|
+
}}
|
|
2166
|
+
/>
|
|
2167
|
+
</div>
|
|
2168
|
+
|
|
2169
|
+
<div className="flex flex-col" style={{ gap: '4px' }}>
|
|
2170
|
+
<label className="vscode-sidebar-section-title" style={{ padding: 0 }}>Context Window (num_ctx)</label>
|
|
2171
|
+
<input
|
|
2172
|
+
type="number"
|
|
2173
|
+
min="1"
|
|
2174
|
+
placeholder="Ex: 8192"
|
|
2175
|
+
value={editingProject.model_params?.num_ctx !== undefined ? editingProject.model_params.num_ctx : ''}
|
|
2176
|
+
onChange={e => {
|
|
2177
|
+
const val = e.target.value === '' ? undefined : parseInt(e.target.value, 10);
|
|
2178
|
+
setEditingProject(p => {
|
|
2179
|
+
const nextParams = { ...p.model_params };
|
|
2180
|
+
if (val === undefined) delete nextParams.num_ctx;
|
|
2181
|
+
else nextParams.num_ctx = val;
|
|
2182
|
+
return { ...p, model_params: nextParams };
|
|
2183
|
+
});
|
|
2184
|
+
}}
|
|
2185
|
+
/>
|
|
2186
|
+
</div>
|
|
2187
|
+
|
|
2188
|
+
<div className="flex flex-col" style={{ gap: '4px' }}>
|
|
2189
|
+
<label className="vscode-sidebar-section-title" style={{ padding: 0 }}>Top P</label>
|
|
2190
|
+
<input
|
|
2191
|
+
type="number"
|
|
2192
|
+
step="0.05"
|
|
2193
|
+
min="0"
|
|
2194
|
+
max="1"
|
|
2195
|
+
placeholder="Ex: 0.9"
|
|
2196
|
+
value={editingProject.model_params?.top_p !== undefined ? editingProject.model_params.top_p : ''}
|
|
2197
|
+
onChange={e => {
|
|
2198
|
+
const val = e.target.value === '' ? undefined : parseFloat(e.target.value);
|
|
2199
|
+
setEditingProject(p => {
|
|
2200
|
+
const nextParams = { ...p.model_params };
|
|
2201
|
+
if (val === undefined) delete nextParams.top_p;
|
|
2202
|
+
else nextParams.top_p = val;
|
|
2203
|
+
return { ...p, model_params: nextParams };
|
|
2204
|
+
});
|
|
2205
|
+
}}
|
|
2206
|
+
/>
|
|
2207
|
+
</div>
|
|
2208
|
+
|
|
2209
|
+
<div className="flex flex-col" style={{ gap: '4px' }}>
|
|
2210
|
+
<label className="vscode-sidebar-section-title" style={{ padding: 0 }}>Frequency Penalty</label>
|
|
2211
|
+
<input
|
|
2212
|
+
type="number"
|
|
2213
|
+
step="0.1"
|
|
2214
|
+
min="-2"
|
|
2215
|
+
max="2"
|
|
2216
|
+
placeholder="Ex: 0.0"
|
|
2217
|
+
value={editingProject.model_params?.frequency_penalty !== undefined ? editingProject.model_params.frequency_penalty : ''}
|
|
2218
|
+
onChange={e => {
|
|
2219
|
+
const val = e.target.value === '' ? undefined : parseFloat(e.target.value);
|
|
2220
|
+
setEditingProject(p => {
|
|
2221
|
+
const nextParams = { ...p.model_params };
|
|
2222
|
+
if (val === undefined) delete nextParams.frequency_penalty;
|
|
2223
|
+
else nextParams.frequency_penalty = val;
|
|
2224
|
+
return { ...p, model_params: nextParams };
|
|
2225
|
+
});
|
|
2226
|
+
}}
|
|
2227
|
+
/>
|
|
2228
|
+
</div>
|
|
2229
|
+
|
|
2230
|
+
<div className="flex flex-col" style={{ gap: '4px' }}>
|
|
2231
|
+
<label className="vscode-sidebar-section-title" style={{ padding: 0 }}>Presence Penalty</label>
|
|
2232
|
+
<input
|
|
2233
|
+
type="number"
|
|
2234
|
+
step="0.1"
|
|
2235
|
+
min="-2"
|
|
2236
|
+
max="2"
|
|
2237
|
+
placeholder="Ex: 0.0"
|
|
2238
|
+
value={editingProject.model_params?.presence_penalty !== undefined ? editingProject.model_params.presence_penalty : ''}
|
|
2239
|
+
onChange={e => {
|
|
2240
|
+
const val = e.target.value === '' ? undefined : parseFloat(e.target.value);
|
|
2241
|
+
setEditingProject(p => {
|
|
2242
|
+
const nextParams = { ...p.model_params };
|
|
2243
|
+
if (val === undefined) delete nextParams.presence_penalty;
|
|
2244
|
+
else nextParams.presence_penalty = val;
|
|
2245
|
+
return { ...p, model_params: nextParams };
|
|
2246
|
+
});
|
|
2247
|
+
}}
|
|
2248
|
+
/>
|
|
2249
|
+
</div>
|
|
2250
|
+
</div>
|
|
2251
|
+
)}
|
|
2252
|
+
</div>
|
|
2253
|
+
|
|
1964
2254
|
{/* Actions */}
|
|
1965
2255
|
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: '8px', paddingTop: '12px', borderTop: '1px solid #3c3c3c', marginTop: '4px' }}>
|
|
1966
2256
|
<button
|
|
@@ -2212,7 +2502,7 @@ export default function App() {
|
|
|
2212
2502
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '14px', color: '#cccccc' }}>
|
|
2213
2503
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
|
|
2214
2504
|
<span className="vscode-sidebar-section-title" style={{ padding: 0 }}>Versão</span>
|
|
2215
|
-
<span style={{ fontSize: '13px', fontWeight: 'bold', color: '#ffffff' }}>0.1.
|
|
2505
|
+
<span style={{ fontSize: '13px', fontWeight: 'bold', color: '#ffffff' }}>0.1.20 alfa</span>
|
|
2216
2506
|
</div>
|
|
2217
2507
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
|
|
2218
2508
|
<span className="vscode-sidebar-section-title" style={{ padding: 0 }}>Autor</span>
|
|
@@ -649,6 +649,13 @@ input:focus, select:focus, textarea:focus {
|
|
|
649
649
|
user-select: none;
|
|
650
650
|
}
|
|
651
651
|
|
|
652
|
+
/* Disable copying text in chat messages */
|
|
653
|
+
.vscode-chat-history,
|
|
654
|
+
.vscode-chat-msg-content,
|
|
655
|
+
.vscode-chat-msg-header {
|
|
656
|
+
user-select: none;
|
|
657
|
+
}
|
|
658
|
+
|
|
652
659
|
|
|
653
660
|
|
|
654
661
|
|
|
@@ -190,6 +190,21 @@ async def handle_run(data: dict):
|
|
|
190
190
|
# Setup project context if provided
|
|
191
191
|
if "project_path" in data or "project_name" in data:
|
|
192
192
|
await handle_load_project(data)
|
|
193
|
+
|
|
194
|
+
if current_project and current_project.project_path:
|
|
195
|
+
state_dir = os.path.join(current_project.project_path, ".opalacoder")
|
|
196
|
+
os.makedirs(state_dir, exist_ok=True)
|
|
197
|
+
state_file = os.path.join(state_dir, "_editor_state.json")
|
|
198
|
+
editor_state = {
|
|
199
|
+
"current_file": data.get("current_file", ""),
|
|
200
|
+
"editor_content": data.get("editor_content", ""),
|
|
201
|
+
"selected_text": data.get("selected_text", "")
|
|
202
|
+
}
|
|
203
|
+
try:
|
|
204
|
+
with open(state_file, "w", encoding="utf-8") as f:
|
|
205
|
+
json.dump(editor_state, f, indent=2, ensure_ascii=False)
|
|
206
|
+
except Exception as e:
|
|
207
|
+
print(f"Warning: Failed to save editor state: {e}", file=sys.stderr)
|
|
193
208
|
|
|
194
209
|
# Intercept and process slash commands (like /help, /undo, /commit)
|
|
195
210
|
if prompt.startswith("/"):
|
|
@@ -244,7 +244,12 @@ async def cmd_models(state: REPLState, _args: list[str]) -> None:
|
|
|
244
244
|
T.console.print(f"\n[dim]Models for project '{state.display_name}':[/dim]")
|
|
245
245
|
T.console.print(f" [cyan]main[/cyan] {main_model}")
|
|
246
246
|
T.console.print(f" [cyan]alternative[/cyan] {alt_model} [dim]({alt_origin})[/dim]")
|
|
247
|
-
|
|
247
|
+
params = getattr(state.project, "model_params", {})
|
|
248
|
+
if params:
|
|
249
|
+
T.console.print(f" [cyan]parameters[/cyan]")
|
|
250
|
+
for k, v in params.items():
|
|
251
|
+
T.console.print(f" {k}: {v}")
|
|
252
|
+
T.console.print(f"\n[dim]Change with /set-main-model <id>, /set-alternative-model <id>, or /set-model-param <name> <value>.[/dim]\n")
|
|
248
253
|
|
|
249
254
|
|
|
250
255
|
@_registry.register("/set-main-model", usage="<model_id>",
|
|
@@ -273,6 +278,52 @@ async def cmd_set_alternative_model(state: REPLState, args: list[str]) -> str |
|
|
|
273
278
|
T.success(f"Alternative model set to '{model_id}' for this project.")
|
|
274
279
|
|
|
275
280
|
|
|
281
|
+
@_registry.register("/set-model-param", usage="<param_name> <value>",
|
|
282
|
+
description="Set advanced model parameter (temperature, max_tokens, num_ctx, top_p, frequency_penalty, presence_penalty)")
|
|
283
|
+
async def cmd_set_model_param(state: REPLState, args: list[str]) -> str | None:
|
|
284
|
+
if len(args) < 2:
|
|
285
|
+
T.error("Usage: /set-model-param <param_name> <value>\n"
|
|
286
|
+
"Allowed params: temperature, max_tokens, num_ctx, top_p, frequency_penalty, presence_penalty")
|
|
287
|
+
return "continue"
|
|
288
|
+
|
|
289
|
+
param = args[0].strip().lower()
|
|
290
|
+
val_str = args[1].strip()
|
|
291
|
+
|
|
292
|
+
allowed_params = {"temperature", "max_tokens", "num_ctx", "top_p", "frequency_penalty", "presence_penalty"}
|
|
293
|
+
if param not in allowed_params:
|
|
294
|
+
T.error(f"Unknown parameter '{param}'. Allowed parameters: {', '.join(allowed_params)}")
|
|
295
|
+
return "continue"
|
|
296
|
+
|
|
297
|
+
try:
|
|
298
|
+
if param in {"max_tokens", "num_ctx"}:
|
|
299
|
+
val = int(val_str)
|
|
300
|
+
if val <= 0:
|
|
301
|
+
raise ValueError("Must be a positive integer")
|
|
302
|
+
elif param == "temperature":
|
|
303
|
+
val = float(val_str)
|
|
304
|
+
if not (0.0 <= val <= 2.0):
|
|
305
|
+
raise ValueError("Must be between 0.0 and 2.0")
|
|
306
|
+
elif param == "top_p":
|
|
307
|
+
val = float(val_str)
|
|
308
|
+
if not (0.0 <= val <= 1.0):
|
|
309
|
+
raise ValueError("Must be between 0.0 and 1.0")
|
|
310
|
+
elif param in {"frequency_penalty", "presence_penalty"}:
|
|
311
|
+
val = float(val_str)
|
|
312
|
+
if not (-2.0 <= val <= 2.0):
|
|
313
|
+
raise ValueError("Must be between -2.0 and 2.0")
|
|
314
|
+
except ValueError as e:
|
|
315
|
+
T.error(f"Invalid value for '{param}': {e}")
|
|
316
|
+
return "continue"
|
|
317
|
+
|
|
318
|
+
if not hasattr(state.project, "model_params") or state.project.model_params is None:
|
|
319
|
+
state.project.model_params = {}
|
|
320
|
+
|
|
321
|
+
state.project.model_params[param] = val
|
|
322
|
+
state.store.save(state.project)
|
|
323
|
+
_rebuild_memgpt(state)
|
|
324
|
+
T.success(f"Model parameter '{param}' set to {val} for this project.")
|
|
325
|
+
|
|
326
|
+
|
|
276
327
|
@_registry.register("/undo", description=_("undo_desc"))
|
|
277
328
|
async def cmd_undo(state: REPLState, _args: list[str]) -> str | None:
|
|
278
329
|
from .vcs import get_vcs_strategy
|
|
@@ -119,14 +119,23 @@ def get_agent_llm_kwargs(agent_name: str) -> dict:
|
|
|
119
119
|
"""Return merged litellm kwargs for *agent_name*.
|
|
120
120
|
|
|
121
121
|
Priority (highest first):
|
|
122
|
-
1.
|
|
123
|
-
2.
|
|
124
|
-
3.
|
|
122
|
+
1. Project-specific model_params (dynamically merged if session exists)
|
|
123
|
+
2. Per-agent override in agents.yaml ``agents.<name>``
|
|
124
|
+
3. Global ``llm_defaults`` in agents.yaml
|
|
125
|
+
4. Hard-coded defaults above
|
|
125
126
|
|
|
126
127
|
Non-litellm fields (model, max_heartbeats) are excluded.
|
|
127
128
|
"""
|
|
128
129
|
merged = dict(_LLM_DEFAULTS)
|
|
129
130
|
merged.update(_AGENT_OVERRIDES.get(agent_name, {}))
|
|
131
|
+
|
|
132
|
+
try:
|
|
133
|
+
from .tools import _PROJECT_SESSION
|
|
134
|
+
if _PROJECT_SESSION and hasattr(_PROJECT_SESSION, "model_params") and _PROJECT_SESSION.model_params:
|
|
135
|
+
merged.update(_PROJECT_SESSION.model_params)
|
|
136
|
+
except Exception:
|
|
137
|
+
pass
|
|
138
|
+
|
|
130
139
|
for field in _NON_LITELLM_FIELDS:
|
|
131
140
|
merged.pop(field, None)
|
|
132
141
|
return merged
|