askmycode 0.1.0__tar.gz → 0.1.1__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.
- {askmycode-0.1.0 → askmycode-0.1.1}/.github/workflows/askmycode.yml +10 -5
- {askmycode-0.1.0 → askmycode-0.1.1}/PKG-INFO +1 -1
- askmycode-0.1.1/askmycode/__init__.py +1 -0
- {askmycode-0.1.0 → askmycode-0.1.1}/askmycode/cli.py +13 -7
- {askmycode-0.1.0 → askmycode-0.1.1}/askmycode/config.py +5 -3
- {askmycode-0.1.0 → askmycode-0.1.1}/askmycode/indexer.py +1 -2
- {askmycode-0.1.0 → askmycode-0.1.1}/askmycode/llm.py +12 -6
- {askmycode-0.1.0 → askmycode-0.1.1}/pyproject.toml +1 -1
- askmycode-0.1.0/askmycode/__init__.py +0 -1
- {askmycode-0.1.0 → askmycode-0.1.1}/.askmycode.toml +0 -0
- {askmycode-0.1.0 → askmycode-0.1.1}/.claude/settings.local.json +0 -0
- {askmycode-0.1.0 → askmycode-0.1.1}/.gitignore +0 -0
- {askmycode-0.1.0 → askmycode-0.1.1}/README.md +0 -0
- {askmycode-0.1.0 → askmycode-0.1.1}/askmycode/embedder.py +0 -0
- {askmycode-0.1.0 → askmycode-0.1.1}/askmycode/manifest.py +0 -0
- {askmycode-0.1.0 → askmycode-0.1.1}/askmycode/project_config.py +0 -0
- {askmycode-0.1.0 → askmycode-0.1.1}/askmycode/reranker.py +0 -0
- {askmycode-0.1.0 → askmycode-0.1.1}/askmycode/retriever.py +0 -0
- {askmycode-0.1.0 → askmycode-0.1.1}/askmycode/store.py +0 -0
- {askmycode-0.1.0 → askmycode-0.1.1}/demo.gif +0 -0
- {askmycode-0.1.0 → askmycode-0.1.1}/demo.tape +0 -0
|
@@ -19,7 +19,7 @@ jobs:
|
|
|
19
19
|
- name: Checkout PR branch
|
|
20
20
|
uses: actions/checkout@v4
|
|
21
21
|
with:
|
|
22
|
-
ref:
|
|
22
|
+
ref: refs/pull/${{ github.event.issue.number }}/head
|
|
23
23
|
fetch-depth: 0
|
|
24
24
|
|
|
25
25
|
- name: Set up Python
|
|
@@ -37,8 +37,9 @@ jobs:
|
|
|
37
37
|
|
|
38
38
|
- name: Extract question from comment
|
|
39
39
|
id: question
|
|
40
|
+
env:
|
|
41
|
+
COMMENT: ${{ github.event.comment.body }}
|
|
40
42
|
run: |
|
|
41
|
-
COMMENT="${{ github.event.comment.body }}"
|
|
42
43
|
QUESTION="${COMMENT#/ask }"
|
|
43
44
|
echo "question=$QUESTION" >> "$GITHUB_OUTPUT"
|
|
44
45
|
|
|
@@ -46,8 +47,9 @@ jobs:
|
|
|
46
47
|
id: answer
|
|
47
48
|
env:
|
|
48
49
|
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
|
50
|
+
QUESTION: ${{ steps.question.outputs.question }}
|
|
49
51
|
run: |
|
|
50
|
-
ANSWER=$(askmycode ask "$
|
|
52
|
+
ANSWER=$(askmycode ask "$QUESTION" --sources)
|
|
51
53
|
# Store multi-line output safely
|
|
52
54
|
{
|
|
53
55
|
echo "answer<<EOF"
|
|
@@ -57,10 +59,13 @@ jobs:
|
|
|
57
59
|
|
|
58
60
|
- name: Post answer as PR comment
|
|
59
61
|
uses: actions/github-script@v7
|
|
62
|
+
env:
|
|
63
|
+
QUESTION: ${{ steps.question.outputs.question }}
|
|
64
|
+
ANSWER: ${{ steps.answer.outputs.answer }}
|
|
60
65
|
with:
|
|
61
66
|
script: |
|
|
62
|
-
const question =
|
|
63
|
-
const answer =
|
|
67
|
+
const question = process.env.QUESTION;
|
|
68
|
+
const answer = process.env.ANSWER;
|
|
64
69
|
const body = [
|
|
65
70
|
`**Q: ${question}**`,
|
|
66
71
|
"",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: askmycode
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: Ask questions about any codebase using AI — your code stays private
|
|
5
5
|
Project-URL: Homepage, https://github.com/NewtYao/askmycode
|
|
6
6
|
Project-URL: Issues, https://github.com/NewtYao/askmycode/issues
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.1"
|
|
@@ -6,8 +6,6 @@ from google.genai.errors import ClientError, ServerError
|
|
|
6
6
|
from rich.console import Console
|
|
7
7
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
8
8
|
|
|
9
|
-
from google.genai import types as genai_types
|
|
10
|
-
|
|
11
9
|
from askmycode import config, indexer, llm, manifest, project_config, retriever, store
|
|
12
10
|
|
|
13
11
|
app = typer.Typer(
|
|
@@ -108,6 +106,9 @@ def ask(
|
|
|
108
106
|
except ServerError as e:
|
|
109
107
|
console.print(f"\n[red]Gemini server error:[/red] {e}")
|
|
110
108
|
raise typer.Exit(1)
|
|
109
|
+
except Exception as e:
|
|
110
|
+
console.print(f"\n[red]Error:[/red] {e}")
|
|
111
|
+
raise typer.Exit(1)
|
|
111
112
|
|
|
112
113
|
if sources and metadatas:
|
|
113
114
|
seen: set = set()
|
|
@@ -202,7 +203,7 @@ def chat(
|
|
|
202
203
|
|
|
203
204
|
console.print("[bold]askmycode chat[/bold] — type your question, [dim]exit[/dim] or Ctrl+C to quit.\n")
|
|
204
205
|
|
|
205
|
-
history: list[
|
|
206
|
+
history: list[dict] = []
|
|
206
207
|
file_tree = llm.build_file_tree(list(manifest.load(project).keys()))
|
|
207
208
|
|
|
208
209
|
while True:
|
|
@@ -238,11 +239,14 @@ def chat(
|
|
|
238
239
|
except (ClientError, ServerError) as e:
|
|
239
240
|
console.print(f"\n[red]Error:[/red] {e}\n")
|
|
240
241
|
continue
|
|
242
|
+
except Exception as e:
|
|
243
|
+
console.print(f"\n[red]Error:[/red] {e}\n")
|
|
244
|
+
continue
|
|
241
245
|
|
|
242
246
|
# Append this turn to history for follow-up context
|
|
243
247
|
user_turn = f"Context:\n{context}\n\nQuestion: {question}"
|
|
244
|
-
history.append(
|
|
245
|
-
history.append(
|
|
248
|
+
history.append({"role": "user", "content": user_turn})
|
|
249
|
+
history.append({"role": "model", "content": "".join(answer_parts)})
|
|
246
250
|
|
|
247
251
|
|
|
248
252
|
@app.command()
|
|
@@ -282,10 +286,12 @@ app.add_typer(_config_app, name="config")
|
|
|
282
286
|
@_config_app.command("set-key")
|
|
283
287
|
def config_set_key(
|
|
284
288
|
api_key: str = typer.Argument(..., help="Your API key"),
|
|
289
|
+
provider: str = typer.Option("", "--provider", "-p", help="Provider this key is for (default: current provider)"),
|
|
285
290
|
) -> None:
|
|
286
291
|
"""Save your API key to ~/.config/askmycode/config.json."""
|
|
287
|
-
config.
|
|
288
|
-
|
|
292
|
+
prov = provider or config.get_provider()
|
|
293
|
+
config.set_api_key(api_key, prov)
|
|
294
|
+
console.print(f"[green]API key saved for provider '{prov}'.[/green]")
|
|
289
295
|
|
|
290
296
|
|
|
291
297
|
@_config_app.command("set-provider")
|
|
@@ -27,12 +27,14 @@ def get_api_key(provider: str = "gemini") -> str | None:
|
|
|
27
27
|
env_var = _ENV_KEYS.get(provider, f"{provider.upper()}_API_KEY")
|
|
28
28
|
if key := os.environ.get(env_var):
|
|
29
29
|
return key
|
|
30
|
-
|
|
30
|
+
data = _load()
|
|
31
|
+
# per-provider store, fallback to legacy "api_key" for backwards compat
|
|
32
|
+
return data.get("api_keys", {}).get(provider) or data.get("api_key")
|
|
31
33
|
|
|
32
34
|
|
|
33
|
-
def set_api_key(key: str) -> None:
|
|
35
|
+
def set_api_key(key: str, provider: str = "gemini") -> None:
|
|
34
36
|
data = _load()
|
|
35
|
-
data
|
|
37
|
+
data.setdefault("api_keys", {})[provider] = key
|
|
36
38
|
_save(data)
|
|
37
39
|
|
|
38
40
|
|
|
@@ -56,9 +56,8 @@ def _ast_chunks(content: str, lines: list[str], relative: str) -> list[dict] | N
|
|
|
56
56
|
return None
|
|
57
57
|
|
|
58
58
|
top_nodes = [
|
|
59
|
-
n for n in
|
|
59
|
+
n for n in tree.body
|
|
60
60
|
if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef))
|
|
61
|
-
and isinstance(getattr(n, "col_offset", -1), int) and n.col_offset == 0
|
|
62
61
|
]
|
|
63
62
|
if not top_nodes:
|
|
64
63
|
return None
|
|
@@ -2,6 +2,9 @@ from typing import Iterator
|
|
|
2
2
|
from google import genai
|
|
3
3
|
from google.genai import types
|
|
4
4
|
|
|
5
|
+
_GEMINI_ROLE = {"user": "user", "model": "model", "assistant": "model"}
|
|
6
|
+
_OPENAI_ROLE = {"user": "user", "model": "assistant", "assistant": "assistant"}
|
|
7
|
+
|
|
5
8
|
_SYSTEM = """\
|
|
6
9
|
You are a helpful assistant that answers questions about a software codebase.
|
|
7
10
|
You are given relevant code snippets retrieved from the codebase and a question.
|
|
@@ -41,13 +44,16 @@ def _stream_gemini(
|
|
|
41
44
|
question: str,
|
|
42
45
|
context: str,
|
|
43
46
|
api_key: str,
|
|
44
|
-
history: list[
|
|
47
|
+
history: list[dict] | None,
|
|
45
48
|
file_tree: str,
|
|
46
49
|
model: str,
|
|
47
50
|
) -> Iterator[str]:
|
|
48
51
|
client = genai.Client(api_key=api_key)
|
|
49
52
|
user_turn = _build_user_turn(question, context, file_tree)
|
|
50
|
-
contents =
|
|
53
|
+
contents = [
|
|
54
|
+
_make_turn(_GEMINI_ROLE.get(h["role"], h["role"]), h["content"])
|
|
55
|
+
for h in (history or [])
|
|
56
|
+
] + [_make_turn("user", user_turn)]
|
|
51
57
|
for chunk in client.models.generate_content_stream(
|
|
52
58
|
model=model,
|
|
53
59
|
contents=contents,
|
|
@@ -72,7 +78,7 @@ def _stream_openai(
|
|
|
72
78
|
user_turn = _build_user_turn(question, context, file_tree)
|
|
73
79
|
messages = [{"role": "system", "content": _SYSTEM}]
|
|
74
80
|
for h in history or []:
|
|
75
|
-
messages.append({"role": h["role"], "content": h["content"]})
|
|
81
|
+
messages.append({"role": _OPENAI_ROLE.get(h["role"], h["role"]), "content": h["content"]})
|
|
76
82
|
messages.append({"role": "user", "content": user_turn})
|
|
77
83
|
with client.chat.completions.create(model=model, messages=messages, stream=True) as stream:
|
|
78
84
|
for chunk in stream:
|
|
@@ -96,7 +102,7 @@ def _stream_anthropic(
|
|
|
96
102
|
user_turn = _build_user_turn(question, context, file_tree)
|
|
97
103
|
messages = []
|
|
98
104
|
for h in history or []:
|
|
99
|
-
messages.append({"role": h["role"], "content": h["content"]})
|
|
105
|
+
messages.append({"role": _OPENAI_ROLE.get(h["role"], h["role"]), "content": h["content"]})
|
|
100
106
|
messages.append({"role": "user", "content": user_turn})
|
|
101
107
|
with client.messages.stream(
|
|
102
108
|
model=model,
|
|
@@ -122,7 +128,7 @@ def _stream_ollama(
|
|
|
122
128
|
user_turn = _build_user_turn(question, context, file_tree)
|
|
123
129
|
messages = [{"role": "system", "content": _SYSTEM}]
|
|
124
130
|
for h in history or []:
|
|
125
|
-
messages.append({"role": h["role"], "content": h["content"]})
|
|
131
|
+
messages.append({"role": _OPENAI_ROLE.get(h["role"], h["role"]), "content": h["content"]})
|
|
126
132
|
messages.append({"role": "user", "content": user_turn})
|
|
127
133
|
resp = requests.post(
|
|
128
134
|
f"{base_url}/api/chat",
|
|
@@ -145,7 +151,7 @@ def answer_stream(
|
|
|
145
151
|
question: str,
|
|
146
152
|
context: str,
|
|
147
153
|
api_key: str,
|
|
148
|
-
history=None,
|
|
154
|
+
history: list[dict] | None = None,
|
|
149
155
|
file_tree: str = "",
|
|
150
156
|
provider: str = "gemini",
|
|
151
157
|
model: str = "",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.0"
|
|
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
|