codebase-brain 0.3.3__tar.gz → 0.3.4__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.
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/PKG-INFO +1 -1
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/brain_parser/codebase_walker.py +1 -1
- codebase_brain-0.3.4/brain_parser/llm_router.py +144 -0
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/codebase_brain.egg-info/PKG-INFO +1 -1
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/setup.py +1 -1
- codebase_brain-0.3.3/brain_parser/llm_router.py +0 -96
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/README.md +0 -0
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/brain_cli.py +0 -0
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/brain_parser/__init__.py +0 -0
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/brain_parser/ast_parser.py +0 -0
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/brain_parser/bug_detector.py +0 -0
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/brain_parser/file_watcher.py +0 -0
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/brain_parser/graph_builder.py +0 -0
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/brain_parser/query_engine.py +0 -0
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/brain_parser/root_cause.py +0 -0
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/brain_parser/universal_parser.py +0 -0
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/codebase_brain.egg-info/SOURCES.txt +0 -0
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/codebase_brain.egg-info/dependency_links.txt +0 -0
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/codebase_brain.egg-info/entry_points.txt +0 -0
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/codebase_brain.egg-info/requires.txt +0 -0
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/codebase_brain.egg-info/top_level.txt +0 -0
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/setup.cfg +0 -0
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/tests/test_bugs.py +0 -0
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/tests/test_classes.py +0 -0
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/tests/test_parser.py +0 -0
- {codebase_brain-0.3.3 → codebase_brain-0.3.4}/tests/test_treesitter.py +0 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from dotenv import load_dotenv
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def get_config():
|
|
6
|
+
"""Load from local .env first, then global ~/.codebase-brain/config.env"""
|
|
7
|
+
local_env = os.path.join(os.getcwd(), '.env')
|
|
8
|
+
if os.path.exists(local_env):
|
|
9
|
+
load_dotenv(local_env, override=True)
|
|
10
|
+
|
|
11
|
+
has_key = os.getenv("LLM_API_KEY") or os.getenv("GROQ_API_KEY")
|
|
12
|
+
|
|
13
|
+
if not has_key:
|
|
14
|
+
global_config = os.path.expanduser("~/.codebase-brain/config.env")
|
|
15
|
+
if os.path.exists(global_config):
|
|
16
|
+
load_dotenv(global_config, override=True)
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
'provider': os.getenv("LLM_PROVIDER", "groq").lower(),
|
|
20
|
+
'api_key': os.getenv("LLM_API_KEY") or os.getenv("GROQ_API_KEY"),
|
|
21
|
+
'model': os.getenv("LLM_MODEL") or os.getenv("GROQ_MODEL", "openai/gpt-oss-120b")
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def call_llm(prompt, max_tokens=500):
|
|
26
|
+
config = get_config()
|
|
27
|
+
provider = config['provider']
|
|
28
|
+
api_key = config['api_key']
|
|
29
|
+
model = config['model']
|
|
30
|
+
|
|
31
|
+
if provider == "groq":
|
|
32
|
+
return _call_groq(api_key, model, prompt, max_tokens)
|
|
33
|
+
elif provider == "openai":
|
|
34
|
+
return _call_openai(api_key, model, prompt, max_tokens)
|
|
35
|
+
elif provider == "anthropic":
|
|
36
|
+
return _call_anthropic(api_key, model, prompt, max_tokens)
|
|
37
|
+
elif provider == "google":
|
|
38
|
+
return _call_google(api_key, model, prompt, max_tokens)
|
|
39
|
+
elif provider == "ollama":
|
|
40
|
+
return _call_ollama(model, prompt, max_tokens)
|
|
41
|
+
else:
|
|
42
|
+
return _call_groq(api_key, model, prompt, max_tokens)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _try_models(model, fallback_list, call_fn):
|
|
46
|
+
"""Generic fallback runner — tries the configured model first, then known-stable alternatives."""
|
|
47
|
+
models_to_try = [model] + [m for m in fallback_list if m != model]
|
|
48
|
+
seen = set()
|
|
49
|
+
models_to_try = [m for m in models_to_try if not (m in seen or seen.add(m))]
|
|
50
|
+
|
|
51
|
+
last_error = None
|
|
52
|
+
for try_model in models_to_try:
|
|
53
|
+
try:
|
|
54
|
+
return call_fn(try_model)
|
|
55
|
+
except Exception as e:
|
|
56
|
+
last_error = e
|
|
57
|
+
continue
|
|
58
|
+
|
|
59
|
+
return f"Could not get AI response — all models unavailable: {str(last_error)}"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _call_groq(api_key, model, prompt, max_tokens):
|
|
63
|
+
from groq import Groq
|
|
64
|
+
client = Groq(api_key=api_key)
|
|
65
|
+
|
|
66
|
+
fallback_list = ["openai/gpt-oss-120b", "llama-3.1-8b-instant", "qwen3.6-27b"]
|
|
67
|
+
|
|
68
|
+
def call_fn(m):
|
|
69
|
+
response = client.chat.completions.create(
|
|
70
|
+
model=m,
|
|
71
|
+
messages=[{"role": "user", "content": prompt}],
|
|
72
|
+
max_tokens=max_tokens,
|
|
73
|
+
temperature=0
|
|
74
|
+
)
|
|
75
|
+
return response.choices[0].message.content
|
|
76
|
+
|
|
77
|
+
return _try_models(model, fallback_list, call_fn)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _call_openai(api_key, model, prompt, max_tokens):
|
|
81
|
+
from openai import OpenAI
|
|
82
|
+
client = OpenAI(api_key=api_key)
|
|
83
|
+
|
|
84
|
+
fallback_list = ["gpt-4o-mini", "gpt-4o", "gpt-3.5-turbo"]
|
|
85
|
+
|
|
86
|
+
def call_fn(m):
|
|
87
|
+
response = client.chat.completions.create(
|
|
88
|
+
model=m,
|
|
89
|
+
messages=[{"role": "user", "content": prompt}],
|
|
90
|
+
max_tokens=max_tokens,
|
|
91
|
+
temperature=0
|
|
92
|
+
)
|
|
93
|
+
return response.choices[0].message.content
|
|
94
|
+
|
|
95
|
+
return _try_models(model, fallback_list, call_fn)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _call_anthropic(api_key, model, prompt, max_tokens):
|
|
99
|
+
import anthropic
|
|
100
|
+
client = anthropic.Anthropic(api_key=api_key)
|
|
101
|
+
|
|
102
|
+
fallback_list = ["claude-3-5-haiku-20241022", "claude-3-5-sonnet-20241022", "claude-3-haiku-20240307"]
|
|
103
|
+
|
|
104
|
+
def call_fn(m):
|
|
105
|
+
response = client.messages.create(
|
|
106
|
+
model=m,
|
|
107
|
+
max_tokens=max_tokens,
|
|
108
|
+
messages=[{"role": "user", "content": prompt}]
|
|
109
|
+
)
|
|
110
|
+
return response.content[0].text
|
|
111
|
+
|
|
112
|
+
return _try_models(model, fallback_list, call_fn)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _call_google(api_key, model, prompt, max_tokens):
|
|
116
|
+
import google.generativeai as genai
|
|
117
|
+
genai.configure(api_key=api_key)
|
|
118
|
+
|
|
119
|
+
fallback_list = ["gemini-1.5-flash", "gemini-1.5-pro", "gemini-2.0-flash"]
|
|
120
|
+
|
|
121
|
+
def call_fn(m):
|
|
122
|
+
gemini = genai.GenerativeModel(m)
|
|
123
|
+
response = gemini.generate_content(prompt)
|
|
124
|
+
return response.text
|
|
125
|
+
|
|
126
|
+
return _try_models(model, fallback_list, call_fn)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _call_ollama(model, prompt, max_tokens):
|
|
130
|
+
import requests
|
|
131
|
+
|
|
132
|
+
fallback_list = ["llama3.2", "codellama", "deepseek-coder"]
|
|
133
|
+
|
|
134
|
+
def call_fn(m):
|
|
135
|
+
response = requests.post(
|
|
136
|
+
"http://localhost:11434/api/generate",
|
|
137
|
+
json={"model": m, "prompt": prompt, "stream": False}
|
|
138
|
+
)
|
|
139
|
+
result = response.json().get("response", "")
|
|
140
|
+
if not result:
|
|
141
|
+
raise ValueError(f"Empty response from Ollama model {m}")
|
|
142
|
+
return result
|
|
143
|
+
|
|
144
|
+
return _try_models(model, fallback_list, call_fn)
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
from dotenv import load_dotenv
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def get_config():
|
|
6
|
-
"""Load from local .env first, then global ~/.codebase-brain/config.env"""
|
|
7
|
-
local_env = os.path.join(os.getcwd(), '.env')
|
|
8
|
-
if os.path.exists(local_env):
|
|
9
|
-
load_dotenv(local_env, override=True)
|
|
10
|
-
|
|
11
|
-
# check if local .env actually had Brain's keys
|
|
12
|
-
has_key = os.getenv("LLM_API_KEY") or os.getenv("GROQ_API_KEY")
|
|
13
|
-
|
|
14
|
-
if not has_key:
|
|
15
|
-
# local .env didn't have Brain's config — fall back to global
|
|
16
|
-
global_config = os.path.expanduser("~/.codebase-brain/config.env")
|
|
17
|
-
if os.path.exists(global_config):
|
|
18
|
-
load_dotenv(global_config, override=True)
|
|
19
|
-
|
|
20
|
-
return {
|
|
21
|
-
'provider': os.getenv("LLM_PROVIDER", "groq").lower(),
|
|
22
|
-
'api_key': os.getenv("LLM_API_KEY") or os.getenv("GROQ_API_KEY"),
|
|
23
|
-
'model': os.getenv("LLM_MODEL") or os.getenv("GROQ_MODEL", "llama-3.3-70b-versatile")
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def call_llm(prompt, max_tokens=500):
|
|
28
|
-
config = get_config()
|
|
29
|
-
provider = config['provider']
|
|
30
|
-
api_key = config['api_key']
|
|
31
|
-
model = config['model']
|
|
32
|
-
|
|
33
|
-
if provider == "groq":
|
|
34
|
-
return _call_groq(api_key, model, prompt, max_tokens)
|
|
35
|
-
elif provider == "openai":
|
|
36
|
-
return _call_openai(api_key, model, prompt, max_tokens)
|
|
37
|
-
elif provider == "anthropic":
|
|
38
|
-
return _call_anthropic(api_key, model, prompt, max_tokens)
|
|
39
|
-
elif provider == "google":
|
|
40
|
-
return _call_google(api_key, model, prompt, max_tokens)
|
|
41
|
-
elif provider == "ollama":
|
|
42
|
-
return _call_ollama(model, prompt, max_tokens)
|
|
43
|
-
else:
|
|
44
|
-
return _call_groq(api_key, model, prompt, max_tokens)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def _call_groq(api_key, model, prompt, max_tokens):
|
|
48
|
-
from groq import Groq
|
|
49
|
-
client = Groq(api_key=api_key)
|
|
50
|
-
response = client.chat.completions.create(
|
|
51
|
-
model=model,
|
|
52
|
-
messages=[{"role": "user", "content": prompt}],
|
|
53
|
-
max_tokens=max_tokens,
|
|
54
|
-
temperature=0
|
|
55
|
-
)
|
|
56
|
-
return response.choices[0].message.content
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def _call_openai(api_key, model, prompt, max_tokens):
|
|
60
|
-
from openai import OpenAI
|
|
61
|
-
client = OpenAI(api_key=api_key)
|
|
62
|
-
response = client.chat.completions.create(
|
|
63
|
-
model=model,
|
|
64
|
-
messages=[{"role": "user", "content": prompt}],
|
|
65
|
-
max_tokens=max_tokens,
|
|
66
|
-
temperature=0
|
|
67
|
-
)
|
|
68
|
-
return response.choices[0].message.content
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def _call_anthropic(api_key, model, prompt, max_tokens):
|
|
72
|
-
import anthropic
|
|
73
|
-
client = anthropic.Anthropic(api_key=api_key)
|
|
74
|
-
response = client.messages.create(
|
|
75
|
-
model=model,
|
|
76
|
-
max_tokens=max_tokens,
|
|
77
|
-
messages=[{"role": "user", "content": prompt}]
|
|
78
|
-
)
|
|
79
|
-
return response.content[0].text
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def _call_google(api_key, model, prompt, max_tokens):
|
|
83
|
-
import google.generativeai as genai
|
|
84
|
-
genai.configure(api_key=api_key)
|
|
85
|
-
gemini = genai.GenerativeModel(model)
|
|
86
|
-
response = gemini.generate_content(prompt)
|
|
87
|
-
return response.text
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def _call_ollama(model, prompt, max_tokens):
|
|
91
|
-
import requests
|
|
92
|
-
response = requests.post(
|
|
93
|
-
"http://localhost:11434/api/generate",
|
|
94
|
-
json={"model": model, "prompt": prompt, "stream": False}
|
|
95
|
-
)
|
|
96
|
-
return response.json().get("response", "")
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|