coreinsight-cli 0.2.6__tar.gz → 0.2.8__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.
Files changed (30) hide show
  1. {coreinsight_cli-0.2.6/coreinsight_cli.egg-info → coreinsight_cli-0.2.8}/PKG-INFO +2 -1
  2. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/coreinsight/analyzer.py +106 -15
  3. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/coreinsight/config.py +3 -0
  4. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/coreinsight/main.py +227 -164
  5. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/coreinsight/memory.py +71 -0
  6. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/coreinsight/sandbox.py +4 -3
  7. coreinsight_cli-0.2.8/coreinsight/tui.py +878 -0
  8. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8/coreinsight_cli.egg-info}/PKG-INFO +2 -1
  9. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/coreinsight_cli.egg-info/SOURCES.txt +1 -0
  10. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/coreinsight_cli.egg-info/requires.txt +1 -0
  11. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/pyproject.toml +2 -1
  12. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/LICENSE +0 -0
  13. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/README.md +0 -0
  14. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/coreinsight/Dockerfile.cpp-sandbox +0 -0
  15. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/coreinsight/Dockerfile.python-sandbox +0 -0
  16. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/coreinsight/__init__.py +0 -0
  17. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/coreinsight/demo/__init__.py +0 -0
  18. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/coreinsight/demo/bad_loop.py +0 -0
  19. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/coreinsight/demo/data_processor.py +0 -0
  20. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/coreinsight/demo/slow.cpp +0 -0
  21. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/coreinsight/hardware.py +0 -0
  22. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/coreinsight/indexer.py +0 -0
  23. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/coreinsight/parser.py +0 -0
  24. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/coreinsight/profiler.py +0 -0
  25. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/coreinsight/prompts.py +0 -0
  26. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/coreinsight/scanner.py +0 -0
  27. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/coreinsight_cli.egg-info/dependency_links.txt +0 -0
  28. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/coreinsight_cli.egg-info/entry_points.txt +0 -0
  29. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/coreinsight_cli.egg-info/top_level.txt +0 -0
  30. {coreinsight_cli-0.2.6 → coreinsight_cli-0.2.8}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coreinsight-cli
3
- Version: 0.2.6
3
+ Version: 0.2.8
4
4
  Summary: Local-first AI performance profiler that mathematically verifies optimizations for Python, C++, and CUDA
5
5
  Author: Varun Jani
6
6
  License: GPL-3.0-or-later
@@ -32,6 +32,7 @@ Requires-Dist: langchain-anthropic>=0.1.0
32
32
  Requires-Dist: pydantic>=2.0
33
33
  Requires-Dist: chromadb>=0.5.0
34
34
  Requires-Dist: sentence-transformers>=3.0.0
35
+ Requires-Dist: textual>=0.60.0
35
36
  Requires-Dist: psutil>=5.9
36
37
  Provides-Extra: compat
37
38
  Requires-Dist: pysqlite3-binary>=0.5.0; extra == "compat"
@@ -14,6 +14,35 @@ from langchain_anthropic import ChatAnthropic
14
14
 
15
15
  from coreinsight.prompts import SYSTEM_PROMPT, ANALYSIS_TEMPLATE, HARNESS_ADDENDUM
16
16
 
17
+ # Phrases that appear at the start of a truncated LLM response
18
+ _TRUNCATION_HINTS = (
19
+ "context length",
20
+ "context_length_exceeded",
21
+ "maximum context",
22
+ "token limit",
23
+ "finish_reason: length",
24
+ "finish_reason\":\"length",
25
+ )
26
+
27
+ def _is_truncated(raw: str) -> bool:
28
+ """
29
+ Returns True if the raw LLM output looks like it was cut off mid-generation.
30
+ Catches both explicit error messages and structural truncation signs.
31
+ """
32
+ if not raw or len(raw.strip()) < 20:
33
+ return True
34
+ low = raw.lower()
35
+ if any(hint in low for hint in _TRUNCATION_HINTS):
36
+ return True
37
+ stripped = raw.strip()
38
+ # JSON truncation: opened but never closed
39
+ if stripped.startswith("{") and not stripped.endswith("}"):
40
+ return True
41
+ # Code truncation: opens a block but ends mid-statement
42
+ if stripped.endswith(("...", "/*", "//", "\"", "'")):
43
+ return True
44
+ return False
45
+
17
46
  logger = logging.getLogger(__name__)
18
47
 
19
48
 
@@ -163,12 +192,15 @@ class AnalyzerAgent:
163
192
  self.json_llm = self.base_llm
164
193
 
165
194
  elif provider == "local_server":
166
- base_url = api_keys.get("local_url", "http://localhost:1234/v1")
195
+ from coreinsight.prompts import ModelTier
196
+ base_url = api_keys.get("local_url", "http://localhost:1234/v1")
197
+ _max_tokens = 2048 if model_tier == ModelTier.SMALL else 4096
167
198
  self.base_llm = ChatOpenAI(
168
199
  model=model_name,
169
200
  api_key="not-needed",
170
201
  base_url=base_url,
171
202
  temperature=0.1,
203
+ max_tokens=_max_tokens,
172
204
  model_kwargs={"response_format": {"type": "json_object"}},
173
205
  )
174
206
  self.json_llm = self.base_llm
@@ -196,11 +228,20 @@ class AnalyzerAgent:
196
228
  self.json_llm = self.base_llm
197
229
 
198
230
  else: # Ollama default
231
+ from coreinsight.prompts import ModelTier
232
+ # Small models (7B) typically have 4096 native context.
233
+ # Asking for more causes silent degradation or OOM on the host.
234
+ # Medium/large local models can handle 8192 comfortably.
235
+ _ctx = 4096 if model_tier == ModelTier.SMALL else 8192
236
+ # num_predict: small models need room for JSON + code in one shot.
237
+ # Capping at 2048 for small prevents runaway generation that hits
238
+ # the limit mid-JSON and returns truncated garbage.
239
+ _predict = 2048 if model_tier == ModelTier.SMALL else 4096
199
240
  self.base_llm = ChatOllama(
200
241
  model=model_name,
201
242
  temperature=0.1,
202
- num_predict=4096,
203
- num_ctx=8192,
243
+ num_predict=_predict,
244
+ num_ctx=_ctx,
204
245
  )
205
246
  self.json_llm = self.base_llm.bind(format="json")
206
247
 
@@ -258,14 +299,31 @@ class AnalyzerAgent:
258
299
  def _invoke_code_chain(self, template: str, variables: dict, language: str) -> str:
259
300
  """Shared invocation + extraction logic for harness and fix chains."""
260
301
  chain = PromptTemplate.from_template(template) | self.base_llm
261
- result = chain.invoke(variables)
302
+ try:
303
+ result = chain.invoke(variables)
304
+ except Exception as e:
305
+ err = str(e).lower()
306
+ if any(h in err for h in _TRUNCATION_HINTS):
307
+ raise RuntimeError(
308
+ f"Model hit its context limit. Try a smaller file, fewer functions, "
309
+ f"or a model with a larger context window. Detail: {e}"
310
+ ) from e
311
+ raise
262
312
  raw = result.content if hasattr(result, "content") else str(result)
263
- # Handle Anthropic returning a list of content blocks
264
313
  if isinstance(raw, list):
265
314
  raw = "\n".join(
266
315
  item["text"] if isinstance(item, dict) and "text" in item else str(item)
267
316
  for item in raw
268
317
  )
318
+ if _is_truncated(raw):
319
+ logger.warning(
320
+ f"LLM output appears truncated (len={len(raw)}). "
321
+ f"Model likely hit its context/predict limit."
322
+ )
323
+ raise RuntimeError(
324
+ "Model output was truncated — hit context or token limit. "
325
+ "Try a model with a larger context window, or reduce the function size."
326
+ )
269
327
  return self._extract_executable_code(raw)
270
328
 
271
329
  def generate_harness(
@@ -421,12 +479,14 @@ def _build_llm(provider: str, model_name: str, api_keys: dict):
421
479
  return llm, llm
422
480
 
423
481
  if provider == "local_server":
424
- base_url = api_keys.get("local_url", "http://localhost:1234/v1")
482
+ base_url = api_keys.get("local_url", "http://localhost:1234/v1")
483
+ _max_tokens = api_keys.pop("_predict", 4096) # reuse same key as Ollama path
425
484
  llm = ChatOpenAI(
426
485
  model=model_name,
427
486
  api_key="not-needed",
428
487
  base_url=base_url,
429
488
  temperature=0.1,
489
+ max_tokens=_max_tokens,
430
490
  model_kwargs={"response_format": {"type": "json_object"}},
431
491
  )
432
492
  return llm, llm
@@ -452,16 +512,33 @@ def _build_llm(provider: str, model_name: str, api_keys: dict):
452
512
  )
453
513
  return llm, llm
454
514
 
455
- # Ollama default
515
+ # Ollama default — context and predict budget are passed in from the
516
+ # calling agent which knows its own model_tier.
517
+ # Default to medium-safe values; callers override via kwargs if needed.
518
+ _ctx = api_keys.pop("_ctx", 8192)
519
+ _predict = api_keys.pop("_predict", 4096)
456
520
  base = ChatOllama(
457
521
  model=model_name,
458
522
  temperature=0.1,
459
- num_predict=4096,
460
- num_ctx=8192,
523
+ num_predict=_predict,
524
+ num_ctx=_ctx,
461
525
  )
462
526
  return base, base.bind(format="json")
463
527
 
464
528
 
529
+ def _build_llm_tiered(provider: str, model_name: str, api_keys: dict, model_tier: str):
530
+ """Wraps _build_llm with tier-aware context settings for local providers."""
531
+ from coreinsight.prompts import ModelTier
532
+ keys = dict(api_keys or {})
533
+ if provider == "ollama":
534
+ keys["_ctx"] = 4096 if model_tier == ModelTier.SMALL else 8192
535
+ keys["_predict"] = 2048 if model_tier == ModelTier.SMALL else 4096
536
+ elif provider == "local_server":
537
+ # max_tokens controls response length — context window is server-side
538
+ keys["_predict"] = 2048 if model_tier == ModelTier.SMALL else 4096
539
+ return _build_llm(provider, model_name, keys)
540
+
541
+
465
542
  class BottleneckAgent:
466
543
  """
467
544
  Agent 1 — analysis only.
@@ -480,7 +557,7 @@ class BottleneckAgent:
480
557
  from coreinsight.prompts import BOTTLENECK_TEMPLATE, SYSTEM_PROMPT
481
558
  self.model_tier = model_tier
482
559
  self.parser = JsonOutputParser(pydantic_object=AuditResult)
483
- self._base_llm, self._json_llm = _build_llm(provider, model_name, api_keys)
560
+ self._base_llm, self._json_llm = _build_llm_tiered(provider, model_name, api_keys, model_tier)
484
561
 
485
562
  self._prompt = PromptTemplate(
486
563
  template=BOTTLENECK_TEMPLATE,
@@ -544,7 +621,7 @@ class OptimizerAgent:
544
621
  ) -> None:
545
622
  from coreinsight.prompts import OPTIMIZER_TEMPLATE
546
623
  self.model_tier = model_tier
547
- self._base_llm, _ = _build_llm(provider, model_name, api_keys)
624
+ self._base_llm, _ = _build_llm_tiered(provider, model_name, api_keys, model_tier)
548
625
  self._template = OPTIMIZER_TEMPLATE
549
626
 
550
627
  def _extract_code(self, raw: str) -> str:
@@ -620,7 +697,7 @@ class HarnessAgent:
620
697
  HARNESS_ADDENDUM_MULTI,
621
698
  )
622
699
  self.model_tier = model_tier
623
- self._base_llm, _ = _build_llm(provider, model_name, api_keys)
700
+ self._base_llm, _ = _build_llm_tiered(provider, model_name, api_keys, model_tier)
624
701
  self._harness_tmpl = HARNESS_TEMPLATE_MULTI + HARNESS_ADDENDUM_MULTI.get(model_tier, "")
625
702
  self._fix_tmpl = FIX_TEMPLATE_MULTI + HARNESS_ADDENDUM_MULTI.get(model_tier, "")
626
703
 
@@ -638,14 +715,28 @@ class HarnessAgent:
638
715
 
639
716
  def _invoke(self, template: str, variables: dict) -> str:
640
717
  chain = PromptTemplate.from_template(template) | self._base_llm
641
- result = chain.invoke(variables)
642
- raw = result.content if hasattr(result, "content") else str(result)
718
+ try:
719
+ result = chain.invoke(variables)
720
+ except Exception as e:
721
+ err = str(e).lower()
722
+ if any(h in err for h in _TRUNCATION_HINTS):
723
+ raise RuntimeError(
724
+ f"Model hit its context limit during harness generation. "
725
+ f"Detail: {e}"
726
+ ) from e
727
+ raise
728
+ raw = result.content if hasattr(result, "content") else str(result)
643
729
  if isinstance(raw, list):
644
730
  raw = "\n".join(
645
731
  item["text"] if isinstance(item, dict) and "text" in item
646
732
  else str(item)
647
733
  for item in raw
648
734
  )
735
+ if _is_truncated(raw):
736
+ raise RuntimeError(
737
+ "Harness output was truncated — model hit its token limit. "
738
+ "Switching to fix loop with truncation note."
739
+ )
649
740
  return self._extract_code(raw)
650
741
 
651
742
  def _check_speedup(self, success: bool, logs: str) -> bool:
@@ -738,7 +829,7 @@ class TestCaseAgent:
738
829
  model_tier: str,
739
830
  ) -> None:
740
831
  self.model_tier = model_tier
741
- self._base_llm, _ = _build_llm(provider, model_name, api_keys)
832
+ self._base_llm, _ = _build_llm_tiered(provider, model_name, api_keys, model_tier)
742
833
 
743
834
  def generate(
744
835
  self,
@@ -18,6 +18,7 @@ FREE_TIER_LIMITS = {
18
18
  "max_retries": 2,
19
19
  "num_test_cases": 8,
20
20
  "hardware_profiling": False,
21
+ "max_files": 2,
21
22
  }
22
23
 
23
24
  PRO_TIER_LIMITS = {
@@ -25,6 +26,7 @@ PRO_TIER_LIMITS = {
25
26
  "max_retries": 5,
26
27
  "num_test_cases": 15,
27
28
  "hardware_profiling": True,
29
+ "max_files": None,
28
30
  }
29
31
 
30
32
  SMALL_MODELS = ["llama3.2:3b", "llama3.2:1b", "codellama:7b", "llama3:7b", "mistral:7b"]
@@ -168,6 +170,7 @@ def run_configure(pro_key: str = None, agent_mode: str = None):
168
170
  if provider == "ollama":
169
171
  config["model_name"] = Prompt.ask("Ollama model name", default=config.get("model_name", "llama3.2"))
170
172
  elif provider == "local_server":
173
+ from rich.panel import Panel
171
174
  console.print(Panel(
172
175
  "[bold]Local inference server setup[/bold]\n\n"
173
176
  "CoreInsight talks to any OpenAI-compatible local server.\n"