ayechat-dev 0.36.9.20260204011001__py3-none-any.whl → 0.36.9.20260205232002__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.
@@ -13,12 +13,16 @@ from aye.model.source_collector import collect_sources
13
13
  from aye.model.auth import get_user_config
14
14
  from aye.model.offline_llm_manager import is_offline_model
15
15
  from aye.controller.util import is_truncated_json
16
- from aye.model.config import SYSTEM_PROMPT, MODELS, DEFAULT_MAX_OUTPUT_TOKENS, DEFAULT_CONTEXT_TARGET_KB
16
+ from aye.model.config import SYSTEM_PROMPT, MODELS, DEFAULT_MAX_OUTPUT_TOKENS, DEFAULT_CONTEXT_TARGET_KB, CONTEXT_HARD_LIMIT_KB
17
17
  from aye.model import telemetry
18
18
 
19
19
  import os
20
20
 
21
21
 
22
+ def _is_verbose():
23
+ return get_user_config("verbose", "off").lower() == "on"
24
+
25
+
22
26
  def _is_debug():
23
27
  return get_user_config("debug", "off").lower() == "on"
24
28
 
@@ -77,7 +81,7 @@ def _get_context_hard_limit(model_id: str) -> int:
77
81
  model_config = _get_model_config(model_id)
78
82
  if model_config and "max_prompt_kb" in model_config:
79
83
  return model_config["max_prompt_kb"] * 1024
80
- return 170 * 1024
84
+ return CONTEXT_HARD_LIMIT_KB * 1024
81
85
 
82
86
 
83
87
  def _filter_ground_truth(files: Dict[str, str], conf: Any, verbose: bool) -> Dict[str, str]:
@@ -134,6 +138,8 @@ def _get_rag_context_files(prompt: str, conf: Any, verbose: bool) -> Dict[str, s
134
138
 
135
139
  context_target_size = _get_context_target_size(conf.selected_model)
136
140
  context_hard_limit = _get_context_hard_limit(conf.selected_model)
141
+ #context_target_size = DEFAULT_CONTEXT_TARGET_KB #_get_context_target_size(conf.selected_model)
142
+ #context_hard_limit = CONTEXT_HARD_LIMIT_KB # _get_context_hard_limit(conf.selected_model)
137
143
 
138
144
  if _is_debug():
139
145
  rprint(f"[yellow]Context target: {context_target_size / 1024:.1f}KB, hard limit: {context_hard_limit / 1024:.1f}KB[/]")
@@ -298,50 +304,54 @@ def invoke_llm(
298
304
  model_config = _get_model_config(conf.selected_model)
299
305
  max_output_tokens = model_config.get("max_output_tokens", DEFAULT_MAX_OUTPUT_TOKENS) if model_config else DEFAULT_MAX_OUTPUT_TOKENS
300
306
 
301
- # 1. Try local/offline model plugins first (no streaming UI for local models)
302
- local_response = plugin_manager.handle_command("local_model_invoke", {
303
- "prompt": prompt,
304
- "model_id": conf.selected_model,
305
- "source_files": source_files,
306
- "chat_id": chat_id,
307
- "root": conf.root,
308
- "system_prompt": system_prompt,
309
- "max_output_tokens": max_output_tokens
310
- })
311
-
312
- if local_response is not None:
313
- return LLMResponse(
314
- summary=local_response.get("summary", ""),
315
- updated_files=local_response.get("updated_files", []),
316
- chat_id=None,
317
- source=LLMSource.LOCAL
318
- )
319
-
320
- # 2. API call with spinner + streaming display
321
- if _is_debug():
322
- print(f"[DEBUG] Processing chat message with chat_id={chat_id or -1}, model={conf.selected_model}")
323
-
324
- telemetry_payload = telemetry.build_payload(top_n=20) if telemetry.is_enabled() else None
325
-
326
- # Create spinner - will be stopped when streaming starts
307
+ # Create spinner - will be shown for ALL model types (local, databricks, API)
327
308
  spinner = StoppableSpinner(
328
309
  console,
329
310
  messages=DEFAULT_THINKING_MESSAGES,
330
311
  interval=15.0
331
312
  )
332
313
 
314
+ # For API calls, we also have streaming display
315
+ streaming_display: Optional[StreamingResponseDisplay] = None
316
+
333
317
  def stop_spinner():
334
- """Callback to stop spinner when first content arrives."""
318
+ """Callback to stop spinner when first content arrives (for streaming API)."""
335
319
  spinner.stop()
336
-
337
- # Create streaming display with callback to stop spinner on first content
338
- streaming_display = StreamingResponseDisplay(on_first_content=stop_spinner)
339
- stream_callback = create_streaming_callback(streaming_display)
340
320
 
341
321
  try:
342
- # Start the spinner before the API call
322
+ # Start the spinner before ANY LLM call (local or API)
343
323
  spinner.start()
344
324
 
325
+ # 1. Try local/offline model plugins first
326
+ local_response = plugin_manager.handle_command("local_model_invoke", {
327
+ "prompt": prompt,
328
+ "model_id": conf.selected_model,
329
+ "source_files": source_files,
330
+ "chat_id": chat_id,
331
+ "root": conf.root,
332
+ "system_prompt": system_prompt,
333
+ "max_output_tokens": max_output_tokens
334
+ })
335
+
336
+ if local_response is not None:
337
+ # Local model handled the request - spinner will be stopped in finally block
338
+ return LLMResponse(
339
+ summary=local_response.get("summary", ""),
340
+ updated_files=local_response.get("updated_files", []),
341
+ chat_id=None,
342
+ source=LLMSource.LOCAL
343
+ )
344
+
345
+ # 2. API call with streaming display
346
+ if _is_debug():
347
+ print(f"[DEBUG] Processing chat message with chat_id={chat_id or -1}, model={conf.selected_model}")
348
+
349
+ telemetry_payload = telemetry.build_payload(top_n=20) if telemetry.is_enabled() else None
350
+
351
+ # Create streaming display with callback to stop spinner on first content
352
+ streaming_display = StreamingResponseDisplay(on_first_content=stop_spinner)
353
+ stream_callback = create_streaming_callback(streaming_display)
354
+
345
355
  api_resp = cli_invoke(
346
356
  message=prompt,
347
357
  chat_id=chat_id or -1,
@@ -352,29 +362,29 @@ def invoke_llm(
352
362
  telemetry=telemetry_payload,
353
363
  on_stream_update=stream_callback
354
364
  )
355
- finally:
356
- # Ensure spinner is stopped (in case no streaming content was received)
357
- spinner.stop()
358
-
359
- # Always stop the streaming display when done
360
- if streaming_display.is_active():
361
- streaming_display.stop()
362
365
 
363
- if telemetry_payload is not None:
364
- telemetry.reset()
366
+ if telemetry_payload is not None:
367
+ telemetry.reset()
365
368
 
366
- if _is_debug():
367
- print(f"[DEBUG] Chat message processed, response keys: {api_resp.keys() if api_resp else 'None'}")
369
+ if _is_debug():
370
+ print(f"[DEBUG] Chat message processed, response keys: {api_resp.keys() if api_resp else 'None'}")
368
371
 
369
- # Check if we already displayed the response via streaming
370
- streamed_summary = bool(api_resp.get("_streamed_summary")) if isinstance(api_resp, dict) else False
372
+ # Check if we already displayed the response via streaming
373
+ streamed_summary = bool(api_resp.get("_streamed_summary")) if isinstance(api_resp, dict) else False
371
374
 
372
- # 3. Parse API response
373
- assistant_resp, new_chat_id = _parse_api_response(api_resp)
375
+ # 3. Parse API response
376
+ assistant_resp, new_chat_id = _parse_api_response(api_resp)
374
377
 
375
- return LLMResponse(
376
- summary="" if streamed_summary else assistant_resp.get("answer_summary", ""),
377
- updated_files=assistant_resp.get("source_files", []),
378
- chat_id=new_chat_id,
379
- source=LLMSource.API
380
- )
378
+ return LLMResponse(
379
+ summary="" if streamed_summary else assistant_resp.get("answer_summary", ""),
380
+ updated_files=assistant_resp.get("source_files", []),
381
+ chat_id=new_chat_id,
382
+ source=LLMSource.API
383
+ )
384
+ finally:
385
+ # Ensure spinner is stopped for ALL code paths (local model, API, or error)
386
+ spinner.stop()
387
+
388
+ # Stop the streaming display if it was created and is active
389
+ if streaming_display is not None and streaming_display.is_active():
390
+ streaming_display.stop()
aye/model/config.py CHANGED
@@ -17,12 +17,17 @@ SMALL_PROJECT_FILE_LIMIT = 200
17
17
  # Projects smaller than this will skip RAG and include all files directly.
18
18
  # Set to match default max_prompt_kb (170KB) so all files can fit in context.
19
19
  SMALL_PROJECT_TOTAL_SIZE_LIMIT = 170 * 1024 # 170KB
20
+ #SMALL_PROJECT_TOTAL_SIZE_LIMIT = 100 * 1024 # 170KB
20
21
 
21
22
  # Default maximum output tokens for LLM responses
22
23
  DEFAULT_MAX_OUTPUT_TOKENS = 32000
24
+ #DEFAULT_MAX_OUTPUT_TOKENS = 16000
23
25
 
24
26
  # Default context target size in KB (used when model doesn't specify one)
25
27
  DEFAULT_CONTEXT_TARGET_KB = 150
28
+ #DEFAULT_CONTEXT_TARGET_KB = 20
29
+
30
+ CONTEXT_HARD_LIMIT_KB = 170
26
31
 
27
32
  # Shared system prompt for all LLM interactions
28
33
  SYSTEM_PROMPT = (
@@ -0,0 +1,312 @@
1
+ import os
2
+ import json
3
+ from typing import Dict, Any, Optional
4
+ import httpx
5
+ from pathlib import Path
6
+ import traceback
7
+
8
+ from rich import print as rprint
9
+
10
+ from .plugin_base import Plugin
11
+ from aye.model.config import SYSTEM_PROMPT, MODELS, DEFAULT_MAX_OUTPUT_TOKENS
12
+ from aye.model.auth import get_user_config
13
+ from aye.controller.util import is_truncated_json
14
+
15
+ LLM_TIMEOUT = 600.0
16
+
17
+
18
+ # Message shown when LLM response is truncated due to output token limits
19
+ TRUNCATED_RESPONSE_MESSAGE = (
20
+ "It looks like my response was cut off because it exceeded the output limit. "
21
+ "This usually happens when you ask me to generate or modify many files at once.\n\n"
22
+ "**To fix this, please try:**\n"
23
+ "1. Break your request into smaller parts (e.g., one file at a time)\n"
24
+ "2. Use the `with` command to focus on specific files: `with file1.py, file2.py: your request`\n"
25
+ "3. Ask me to work on fewer files or smaller changes in each request\n\n"
26
+ "For example, instead of 'update all files to add logging', try:\n"
27
+ " `with src/main.py: add logging to this file`"
28
+ )
29
+
30
+
31
+ def _get_model_config(model_id: str) -> Optional[Dict[str, Any]]:
32
+ """Get configuration for a specific model."""
33
+ for model in MODELS:
34
+ if model["id"] == model_id:
35
+ return model
36
+ return None
37
+
38
+
39
+ def _extract_json_object(raw_response: str, prefer_last: bool = True, require_keys=None):
40
+ """
41
+ Best-effort extraction of a JSON object (dict) from a raw LLM response.
42
+
43
+ Handles common failure modes where the model returns:
44
+ - extra commentary before/after JSON
45
+ - multiple JSON objects (e.g., an invalid attempt + a corrected attempt)
46
+
47
+ Args:
48
+ raw_response: raw LLM response string
49
+ prefer_last: when multiple JSON objects exist, return the last parsed object
50
+ require_keys: optional iterable of keys; if provided, only consider objects
51
+ that contain all of these keys
52
+
53
+ Returns:
54
+ dict or None
55
+ """
56
+ # 1) Direct JSON parse
57
+ try:
58
+ obj = json.loads(raw_response)
59
+ if isinstance(obj, dict):
60
+ if require_keys and not all(k in obj for k in require_keys):
61
+ return None
62
+ return obj
63
+ except Exception:
64
+ pass
65
+
66
+ text = str(raw_response)
67
+
68
+ # 2) Scan for balanced JSON object candidates (string/escape-aware)
69
+ candidates = []
70
+ depth = 0
71
+ start = None
72
+ in_str = False
73
+ escape = False
74
+
75
+ for i, ch in enumerate(text):
76
+ if in_str:
77
+ if escape:
78
+ escape = False
79
+ elif ch == '\\':
80
+ escape = True
81
+ elif ch == '"':
82
+ in_str = False
83
+ continue
84
+
85
+ # not in string
86
+ if ch == '"':
87
+ in_str = True
88
+ continue
89
+
90
+ if ch == '{':
91
+ if depth == 0:
92
+ start = i
93
+ depth += 1
94
+ elif ch == '}' and depth > 0:
95
+ depth -= 1
96
+ if depth == 0 and start is not None:
97
+ candidates.append(text[start : i + 1])
98
+ start = None
99
+
100
+ parsed = []
101
+ for cand in candidates:
102
+ try:
103
+ obj = json.loads(cand)
104
+ if not isinstance(obj, dict):
105
+ continue
106
+ if require_keys and not all(k in obj for k in require_keys):
107
+ continue
108
+ parsed.append(obj)
109
+ except Exception:
110
+ continue
111
+
112
+ if not parsed:
113
+ return None
114
+
115
+ return parsed[-1] if prefer_last else parsed[0]
116
+
117
+
118
+ class DatabricksModelPlugin(Plugin):
119
+ name = "databricks_model"
120
+ version = "1.0.0"
121
+ premium = "free"
122
+
123
+ def __init__(self):
124
+ super().__init__()
125
+ self.chat_history: Dict[str, list] = {}
126
+ self.history_file: Optional[Path] = None
127
+
128
+ def init(self, cfg: Dict[str, Any]) -> None:
129
+ """Initialize the local model plugin."""
130
+ super().init(cfg)
131
+ if self.debug:
132
+ rprint(f"[bold yellow]Initializing {self.name} v{self.version}[/]")
133
+
134
+ def _load_history(self) -> None:
135
+ """Load chat history from disk."""
136
+ if not self.history_file:
137
+ if self.verbose:
138
+ rprint("[yellow]History file path not set for local model. Skipping load.[/]")
139
+ self.chat_history = {}
140
+ return
141
+
142
+ if self.history_file.exists():
143
+ try:
144
+ data = json.loads(self.history_file.read_text(encoding="utf-8"))
145
+ self.chat_history = data.get("conversations", {})
146
+ except Exception as e:
147
+ if self.verbose:
148
+ rprint(f"[yellow]Could not load chat history: {e}[/]")
149
+ self.chat_history = {}
150
+ else:
151
+ self.chat_history = {}
152
+
153
+ def _save_history(self) -> None:
154
+ """Save chat history to disk."""
155
+ if not self.history_file:
156
+ if self.verbose:
157
+ rprint("[yellow]History file path not set for local model. Skipping save.[/]")
158
+ return
159
+
160
+ try:
161
+ self.history_file.parent.mkdir(parents=True, exist_ok=True)
162
+ data = {"conversations": self.chat_history}
163
+ self.history_file.write_text(json.dumps(data, indent=2), encoding="utf-8")
164
+ except Exception as e:
165
+ if self.verbose:
166
+ rprint(f"[yellow]Could not save chat history: {e}[/]")
167
+
168
+ def _get_conversation_id(self, chat_id: Optional[int] = None) -> str:
169
+ """Get conversation ID for history tracking."""
170
+ return str(chat_id) if chat_id and chat_id > 0 else "default"
171
+
172
+ def _build_user_message(self, prompt: str, source_files: Dict[str, str]) -> str:
173
+ """Build the user message with optional source files appended."""
174
+ user_message = prompt
175
+ if source_files:
176
+ user_message += "\n\n--- Source files are below. ---\n"
177
+ for file_name, content in source_files.items():
178
+ user_message += f"\n** {file_name} **\n```\n{content}\n```\n"
179
+ return user_message
180
+
181
+ def _parse_llm_response(self, generated_text: str) -> Dict[str, Any]:
182
+ """Parse LLM response text and convert to expected format."""
183
+ try:
184
+ llm_response = json.loads(generated_text)
185
+ except json.JSONDecodeError:
186
+ # Check if this looks like a truncated response
187
+ #if is_truncated_json(generated_text):
188
+ # return {
189
+ # "summary": TRUNCATED_RESPONSE_MESSAGE,
190
+ # "updated_files": []
191
+ # }
192
+
193
+ # Not truncated, just malformed - return as plain text
194
+ llm_response = {
195
+ "answer_summary": generated_text,
196
+ "source_files": []
197
+ }
198
+
199
+ return {
200
+ "summary": llm_response.get("answer_summary", ""),
201
+ "updated_files": [
202
+ {
203
+ "file_name": f.get("file_name"),
204
+ "file_content": f.get("file_content")
205
+ }
206
+ for f in llm_response.get("source_files", [])
207
+ ]
208
+ }
209
+
210
+ def _create_error_response(self, error_msg: str) -> Dict[str, Any]:
211
+ """Create a standardized error response."""
212
+ if self.verbose:
213
+ rprint(f"[red]{error_msg}[/]")
214
+ return {
215
+ "summary": error_msg,
216
+ "updated_files": []
217
+ }
218
+
219
+ def _handle_databricks(self, prompt: str, source_files: Dict[str, str], chat_id: Optional[int] = None, system_prompt: Optional[str] = None, max_output_tokens: int = DEFAULT_MAX_OUTPUT_TOKENS) -> Optional[Dict[str, Any]]:
220
+ api_url = os.environ.get("AYE_DBX_API_URL")
221
+ api_key = os.environ.get("AYE_DBX_API_KEY")
222
+ model_name = os.environ.get("AYE_DBX_MODEL", "gpt-3.5-turbo")
223
+
224
+ if not api_url or not api_key:
225
+ return None
226
+
227
+ conv_id = self._get_conversation_id(chat_id)
228
+ if conv_id not in self.chat_history:
229
+ self.chat_history[conv_id] = []
230
+
231
+ user_message = self._build_user_message(prompt, source_files)
232
+ effective_system_prompt = system_prompt if system_prompt else SYSTEM_PROMPT
233
+
234
+ messages_json = [{"role": "system", "content": effective_system_prompt}] + self.chat_history[conv_id] + [{"role": "user", "content": user_message}]
235
+ messages = messages_json # json.dumps(messages_json)
236
+ if self.debug:
237
+ print(">>>>>>>>>>>>>>>>")
238
+ print(self.chat_history[conv_id])
239
+ print(">>>>>>>>>>>>>>>>")
240
+
241
+ headers = {"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"}
242
+ #payload = {"model": model_name, "messages": messages, "temperature": 0.7, "max_tokens": max_output_tokens, "response_format": {"type": "json_object"}}
243
+ payload = {"model": model_name, "messages": messages, "temperature": 0.7, "max_tokens": max_output_tokens}
244
+
245
+ try:
246
+ with httpx.Client(timeout=LLM_TIMEOUT) as client:
247
+ response = client.post(api_url, json=payload, headers=headers)
248
+ if self.verbose and response.status_code != 200:
249
+ print(f"Status code: {response.status_code}")
250
+ print("-----------------")
251
+ print(response.text)
252
+ print("-----------------")
253
+ response.raise_for_status()
254
+ result = response.json()
255
+ if result.get("choices") and result["choices"][0].get("message"):
256
+ raw_response = result["choices"][0]["message"]["content"]
257
+ generated_json = _extract_json_object(raw_response)
258
+ generated_text = json.dumps(generated_json)
259
+ if self.debug:
260
+ print("-----------------")
261
+ print(response.text)
262
+ print("-----------------")
263
+ print(generated_text)
264
+ print("-----------------")
265
+ #generated_text = result["choices"][0]["message"]["content"][1]["text"]
266
+ self.chat_history[conv_id].append({"role": "user", "content": user_message})
267
+ self.chat_history[conv_id].append({"role": "assistant", "content": generated_text})
268
+ self._save_history()
269
+ #return json.dumps(generated_text)
270
+ return self._parse_llm_response(generated_text)
271
+ return self._create_error_response("Failed to get a valid response from the Databricks API")
272
+ except httpx.HTTPStatusError as e:
273
+ traceback.print_exc()
274
+ error_msg = f"DBX API error: {e.response.status_code}"
275
+ try:
276
+ error_detail = e.response.json()
277
+ if "error" in error_detail:
278
+ error_msg += f" - {error_detail['error'].get('message', str(error_detail['error']))}"
279
+ except: error_msg += f" - {e.response.text[:200]}"
280
+ return self._create_error_response(error_msg)
281
+ except Exception as e:
282
+ traceback.print_exc()
283
+ return self._create_error_response(f"Error calling Databricks API: {str(e)}")
284
+
285
+
286
+ def on_command(self, command_name: str, params: Dict[str, Any]) -> Optional[Dict[str, Any]]:
287
+ if command_name == "new_chat":
288
+ root = params.get("root")
289
+ history_file = Path(root) / ".aye" / "chat_history.json" if root else Path.cwd() / ".aye" / "chat_history.json"
290
+ history_file.unlink(missing_ok=True)
291
+ self.chat_history = {}
292
+ if self.verbose: rprint("[yellow]Local model chat history cleared.[/]")
293
+ return {"status": "local_history_cleared"}
294
+
295
+ if command_name == "local_model_invoke":
296
+ prompt = params.get("prompt", "").strip()
297
+ model_id = params.get("model_id", "")
298
+ source_files = params.get("source_files", {})
299
+ chat_id = params.get("chat_id")
300
+ root = params.get("root")
301
+ system_prompt = params.get("system_prompt")
302
+ max_output_tokens = params.get("max_output_tokens", DEFAULT_MAX_OUTPUT_TOKENS)
303
+
304
+ self.history_file = Path(root) / ".aye" / "chat_history.json" if root else Path.cwd() / ".aye" / "chat_history.json"
305
+ self._load_history()
306
+
307
+ result = self._handle_databricks(prompt, source_files, chat_id, system_prompt, max_output_tokens)
308
+ if result is not None: return result
309
+
310
+ return None
311
+
312
+ return None
@@ -136,48 +136,6 @@ class LocalModelPlugin(Plugin):
136
136
  "updated_files": []
137
137
  }
138
138
 
139
- def _handle_databricks(self, prompt: str, source_files: Dict[str, str], chat_id: Optional[int] = None, system_prompt: Optional[str] = None, max_output_tokens: int = DEFAULT_MAX_OUTPUT_TOKENS) -> Optional[Dict[str, Any]]:
140
- api_url = os.environ.get("AYE_DBX_API_URL")
141
- api_key = os.environ.get("AYE_DBX_API_KEY")
142
- model_name = os.environ.get("AYE_DBX_MODEL", "gpt-3.5-turbo")
143
-
144
- if not api_url or not api_key:
145
- return None
146
-
147
- conv_id = self._get_conversation_id(chat_id)
148
- if conv_id not in self.chat_history:
149
- self.chat_history[conv_id] = []
150
-
151
- user_message = self._build_user_message(prompt, source_files)
152
- effective_system_prompt = system_prompt if system_prompt else SYSTEM_PROMPT
153
-
154
- messages = [{"role": "system", "content": effective_system_prompt}] + self.chat_history[conv_id] + [{"role": "user", "content": user_message}]
155
-
156
- headers = {"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"}
157
- payload = {"model": model_name, "messages": messages, "temperature": 0.7, "max_tokens": max_output_tokens, "response_format": {"type": "json_object"}}
158
-
159
- try:
160
- with httpx.Client(timeout=LLM_TIMEOUT) as client:
161
- response = client.post(api_url, json=payload, headers=headers)
162
- response.raise_for_status()
163
- result = response.json()
164
- if result.get("choices") and result["choices"][0].get("message"):
165
- generated_text = result["choices"][0]["message"]["content"][0]["text"]
166
- self.chat_history[conv_id].append({"role": "user", "content": user_message})
167
- self.chat_history[conv_id].append({"role": "assistant", "content": generated_text})
168
- self._save_history()
169
- return self._parse_llm_response(generated_text)
170
- return self._create_error_response("Failed to get a valid response from the Databricks API")
171
- except httpx.HTTPStatusError as e:
172
- error_msg = f"DBX API error: {e.response.status_code}"
173
- try:
174
- error_detail = e.response.json()
175
- if "error" in error_detail:
176
- error_msg += f" - {error_detail['error'].get('message', str(error_detail['error']))}"
177
- except: error_msg += f" - {e.response.text[:200]}"
178
- return self._create_error_response(error_msg)
179
- except Exception as e:
180
- return self._create_error_response(f"Error calling Databricks API: {str(e)}")
181
139
 
182
140
  def _handle_openai_compatible(self, prompt: str, source_files: Dict[str, str], chat_id: Optional[int] = None, system_prompt: Optional[str] = None, max_output_tokens: int = DEFAULT_MAX_OUTPUT_TOKENS) -> Optional[Dict[str, Any]]:
183
141
  """Handle OpenAI-compatible API endpoints.
@@ -288,9 +246,6 @@ class LocalModelPlugin(Plugin):
288
246
  result = self._handle_openai_compatible(prompt, source_files, chat_id, system_prompt, max_output_tokens)
289
247
  if result is not None: return result
290
248
 
291
- result = self._handle_databricks(prompt, source_files, chat_id, system_prompt, max_output_tokens)
292
- if result is not None: return result
293
-
294
249
  if model_id == "google/gemini-2.5-pro":
295
250
  return self._handle_gemini_pro_25(prompt, source_files, chat_id, system_prompt, max_output_tokens)
296
251
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ayechat-dev
3
- Version: 0.36.9.20260204011001
3
+ Version: 0.36.9.20260205232002
4
4
  Summary: Aye Chat: Terminal-first AI Code Generator
5
5
  Author-email: "Acrotron, Inc." <info@acrotron.com>
6
6
  License: MIT
@@ -6,7 +6,7 @@ aye/controller/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  aye/controller/command_handlers.py,sha256=S_HOYY91lbLkGtCkby28T8qUFPNtfy-CbOGn9VGZtTo,17446
7
7
  aye/controller/commands.py,sha256=sXmK_sgNBrw9Fs7mKcr93-wsu740ZlvWSisQfS-1EUE,12278
8
8
  aye/controller/llm_handler.py,sha256=gY3X2rHcvhPp8iqp_Vor7QpbwHltmcm_Uu9MWF1Z598,7120
9
- aye/controller/llm_invoker.py,sha256=p_Vk2a3YrWKwDupLfSVRinR5llDfq1Fb_f7WrYozK6M,14127
9
+ aye/controller/llm_invoker.py,sha256=1TMsF5q9HT5r8F4zZ5HQo_g_eYyt7uBX4d46QsUHY4k,14868
10
10
  aye/controller/plugin_manager.py,sha256=9ZuITyA5sQJJJU-IntLQ1SsxXsDnbgZKPOF4e9VmsEU,3018
11
11
  aye/controller/repl.py,sha256=nSlzAHD8MQlQ7vnpBTLVzA1jQ8jkNjxUnUoqE31chxY,27028
12
12
  aye/controller/tutorial.py,sha256=lc92jOcJOYCVrrjTEF0Suk4-8jn-ku98kTJEIL8taUA,7254
@@ -16,7 +16,7 @@ aye/model/api.py,sha256=HhSMQQ_szdC2ZPOSfNsJRbs1FRwb6WyYIeLejB2ScbA,13272
16
16
  aye/model/ast_chunker.py,sha256=rVcDdynVUXXyxWVgtUcsee_STqB7SAwP776ktWTlYig,4462
17
17
  aye/model/auth.py,sha256=ozV_uQxdqXtUoWO3nZwpzVnDOIfnRAmSMC6W0N724vE,4800
18
18
  aye/model/autodiff_config.py,sha256=b8pyudkJFYXF5JWPxft0bH5uazeCab9i-A11dNt1d7U,931
19
- aye/model/config.py,sha256=o6bQhj5gqhSqtWD6DLow7NNy6Hdaede02h_xb7uPLXo,9280
19
+ aye/model/config.py,sha256=s9c5w4bmOegpNu2CM1z8ITWUbsEzMaX9_BliqHr7KLA,9430
20
20
  aye/model/download_plugins.py,sha256=6omyFGdxlEIb7tKPLq4rRVrRYeCPUUCE8aZHvJAKGSc,4442
21
21
  aye/model/file_processor.py,sha256=b7YGvHAmhGto9JbtzcfrsdkFtksHbosYt-42EnR22Uo,2131
22
22
  aye/model/ignore_patterns.py,sha256=AhcnZuU9_a77Q4yKFRTG6yIrDG4HcECwdY7D1NQkBDY,2767
@@ -43,7 +43,8 @@ aye/plugins/__init__.py,sha256=dSTxs461ICx0O1tbCBCca0W_7QIAa0Yt9PQhHiT5uZQ,173
43
43
  aye/plugins/at_file_completer.py,sha256=uNS4gWpfKvn9_nGxZbhQVjVg_S82g977gfBR-pL3XrQ,19582
44
44
  aye/plugins/auto_detect_mask.py,sha256=gZKH4qkR-A73uKpMkPXhlgI452Ae_2YG1nHtaIkOvwM,6864
45
45
  aye/plugins/completer.py,sha256=qhxke5Q76P2u0LojSIL3V48RTNG5tWL-5-TK5tNutrE,13893
46
- aye/plugins/local_model.py,sha256=q0RjSjLhEQcDMOCLAK6k1YCW5ECrvdT_g0lKRHMX-AE,14810
46
+ aye/plugins/databricks_model.py,sha256=TpERRIZKh_vJBVu630ModfT2geMXwEEX5XDqzlLt7dI,12494
47
+ aye/plugins/local_model.py,sha256=u3cVLkAD2XGaEvXEFKnmZcDhmqFOi1aqD0ocgysxwCc,12036
47
48
  aye/plugins/offline_llm.py,sha256=qFmd1e8Lbl7yiMgXpXjOQkQTNxOk0_WXU7km2DTKXGY,13357
48
49
  aye/plugins/plugin_base.py,sha256=t5hTOnA0dZC237BnseAgdXbOqErlSCNLUo_Uul09TSw,1673
49
50
  aye/plugins/shell_executor.py,sha256=a0mlZnQeURONdtPM7iageTcQ8PiNLQbjxoY54EsS32o,7502
@@ -54,9 +55,9 @@ aye/presenter/diff_presenter.py,sha256=cbxfOEqGomPTDvQpKdybfYeNUD2DYVAl85j1uy5--
54
55
  aye/presenter/repl_ui.py,sha256=PVENlAQM_tm_k2dANsmQH6I8ATMVXhrdj_hNzc38pSw,8156
55
56
  aye/presenter/streaming_ui.py,sha256=_3tBEuNH9UQ9Gyq2yuvRfX4SWVkcGMYirEUGj-MXVJ0,12768
56
57
  aye/presenter/ui_utils.py,sha256=6KXR4_ZZZUdF5pCHrPqO8yywlQk7AOzWe-2B4Wj_-ZQ,5441
57
- ayechat_dev-0.36.9.20260204011001.dist-info/licenses/LICENSE,sha256=U1ou6lkMKmPo16-E9YowIu3goU7sOWKUprGo0AOA72s,1065
58
- ayechat_dev-0.36.9.20260204011001.dist-info/METADATA,sha256=c6g026ZNTxo1IUHsgXnRCWdyJZnX_ddUTwwo4i4mvtc,7718
59
- ayechat_dev-0.36.9.20260204011001.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
60
- ayechat_dev-0.36.9.20260204011001.dist-info/entry_points.txt,sha256=KGsOma6szoefNN6vHozg3Pbf1fjZ7ZbmwrOiVwBd0Ik,41
61
- ayechat_dev-0.36.9.20260204011001.dist-info/top_level.txt,sha256=7WZL0LOx4-GKKvgU1mtI5s4Dhk2OdieVZZvVnxFJHr8,4
62
- ayechat_dev-0.36.9.20260204011001.dist-info/RECORD,,
58
+ ayechat_dev-0.36.9.20260205232002.dist-info/licenses/LICENSE,sha256=U1ou6lkMKmPo16-E9YowIu3goU7sOWKUprGo0AOA72s,1065
59
+ ayechat_dev-0.36.9.20260205232002.dist-info/METADATA,sha256=pqjcZNYIDp3AmFU5M6Ia7EmlAoIBsNlWbq1Ay2pGLBo,7718
60
+ ayechat_dev-0.36.9.20260205232002.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
61
+ ayechat_dev-0.36.9.20260205232002.dist-info/entry_points.txt,sha256=KGsOma6szoefNN6vHozg3Pbf1fjZ7ZbmwrOiVwBd0Ik,41
62
+ ayechat_dev-0.36.9.20260205232002.dist-info/top_level.txt,sha256=7WZL0LOx4-GKKvgU1mtI5s4Dhk2OdieVZZvVnxFJHr8,4
63
+ ayechat_dev-0.36.9.20260205232002.dist-info/RECORD,,