-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cerebrus #52
Merged
Merged
Cerebrus #52
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
__version__ = "0.7.4" | ||
__version__ = "0.7.5" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import ast | ||
|
||
from spice import Spice, SpiceMessages | ||
|
||
from ragdaemon.graph import KnowledgeGraph | ||
|
||
|
||
def parse_script(response: str) -> tuple[str, str]: | ||
"""Split the response into a message and a script. | ||
|
||
Expected use is: run the script if there is one, otherwise print the message. | ||
""" | ||
# Parse delimiter | ||
n_delimiters = response.count("```") | ||
if n_delimiters < 2: | ||
return response, "" | ||
segments = response.split("```") | ||
message = f"{segments[0]}\n{segments[-1]}" | ||
script = "```".join(segments[1:-1]).strip() # Leave 'inner' delimiters alone | ||
|
||
# Check for common mistakes | ||
if script.split("\n")[0].startswith("python"): | ||
script = "\n".join(script.split("\n")[1:]) | ||
try: # Make sure it's valid python | ||
ast.parse(script) | ||
except SyntaxError: | ||
raise SyntaxError(f"Script contains invalid Python:\n{response}") | ||
return message, script | ||
|
||
|
||
class Printer: | ||
printed: str = "" | ||
answered: str = "" | ||
|
||
def print(self, *args: str): | ||
self.printed += " ".join(str(a) for a in args) + "\n" | ||
|
||
def answer(self, *args: str): | ||
self.answered += " ".join(str(a) for a in args) + "\n" | ||
|
||
|
||
async def cerebrus( | ||
query: str, graph: KnowledgeGraph, spice_client: Spice, leash: bool = False | ||
) -> str: | ||
messages = SpiceMessages(spice_client) | ||
messages.add_system_prompt("cerebrus") | ||
messages.add_user_message(query) | ||
|
||
starting_cost = spice_client.total_cost | ||
printer = Printer() | ||
max_iterations = 10 | ||
answer = "" | ||
for _ in range(max_iterations): | ||
response = await spice_client.get_response(messages=messages) | ||
script = "" | ||
try: | ||
message, script = parse_script(response.text) | ||
if not script: | ||
if message: | ||
answer = message | ||
break | ||
exec( | ||
script, | ||
{"print": printer.print, "answer": printer.answer, "graph": graph}, | ||
) | ||
except KeyboardInterrupt: | ||
raise | ||
except Exception as e: | ||
printer.print(f"Error: {e}") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Catching a general |
||
if not printer.printed: | ||
if printer.answered: | ||
answer = printer.answered | ||
break | ||
next_message = f"Script: {script}\n{80*'-'}\nOutput: {printer.printed}" | ||
messages.add_system_message(next_message) | ||
if leash: | ||
print(next_message) | ||
printer.printed = "" | ||
if leash: | ||
print("Total cost:", spice_client.total_cost - starting_cost) | ||
return answer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
Return a Python script inside ```triple backticks```, and nothing else. | ||
You are part of an automated coding assistant called Ragdaemon. | ||
You iteratively write Python scripts and review their output, and then return an answer to the user. | ||
You have access to three global variables: | ||
- `graph`: A networkx.MultiDiGraph object representing a codebase. | ||
- `print`: An override of Python's default print function that appends to your conversation. | ||
- `answer`: A function that returns text to the user. | ||
|
||
A typical interaction goes like this: | ||
1. The user asks a question | ||
2. You write a script to search the `graph` for some files or keywords from the question and `print()` the output | ||
3. The compiler runs your scripts and adds the output to the conversation | ||
[repeat 2 and 3 until you have found the answer] | ||
4. You compose a clear, concise answer to the user's question based on what you see in the conversation | ||
5. You write a script that with `answer(<your answer here>)` to communicate the answer to the user | ||
|
||
# Graph Structure | ||
### Nodes | ||
Directories, files, chunks (functions, classes or methods) and diffs. They have attributes: | ||
- `id`: Human-readable path, e.g. `path/to/file:class.method` | ||
- `type`: One of "directory", "file", "chunk", "diff" | ||
- `document`: The content of the node. For files, diffs and chunks, it's the text. For directories, it's a list of files. | ||
|
||
### Edges | ||
Have a `type` attribute which is either: | ||
- `hierarchy`: Point from parent to child: directory to files, file to chunks, etc. | ||
- `diff`: Point from a diff root node to diff chunks, and diff chunks to the file/chunks they modify. | ||
|
||
### Notes | ||
- Code files may or may not include chunks. If they do: | ||
- The `path/to/file` has exactly one child, `path/to/file:BASE` (the BASE chunk) | ||
- Top-level functions or classes are children of the BASE chunk. Methods or sub-functions are then children of those. | ||
- Every line of code is contained in exactly one chunk. The BASE chunk acts as a catch-all for imports and space between functions/classes. | ||
|
||
# Instructions | ||
Review the user query and the full conversation history. | ||
If additional information is needed: | ||
1. Write a Python SCRIPT, inside ```triple backticks``` that identifies the next piece of information needed, either using the graph or other Python code, like `subprocess`. | ||
2. Save the relevant output using the `print()` function. | ||
If you have the answer: | ||
1. Write a Python SCRIPT, inside ``triple backticks``` that communicates back to the user using the `answer()` function. | ||
|
||
# Remember: | ||
* ONLY RETURN PYTHON CODE INSIDE TRIPLE BACKTICKS. Anything else will be ignored. | ||
* Don't communicate to the user until you've gathered the necessary context, as calling `answer()` will end the conversation. | ||
* Use the `graph` for inspecting codebase structure and content. | ||
* If you need to run a shell commands, like git, use the `subprocess` module. | ||
* Avoid preamble or narration of what you're doing. Just give the user a concise answer to their question. | ||
* You can include programmatic logic in your answer, e.g. by using `answer` inside a `for` loop or `if` block. | ||
* ONLY COMMUNICATE TO THE USER VIA PYTHON SCRIPT WITH THE `answer()` FUNCTION. Anything else will be ignored. | ||
|
||
# Example | ||
-------------------------------------------------------------------------------- | ||
USER: "What is does get_document do?" | ||
CONVERSATION: [] | ||
|
||
SCRIPT: | ||
``` | ||
print([node for node in graph.nodes if "get_document" in node]) | ||
``` | ||
-------------------------------------------------------------------------------- | ||
USER: "What is does get_document do?" | ||
CONVERSATION: [ | ||
"Script: print([node for node in graph.nodes if 'get_document' in node])\nOutput: ['get_document']\n" | ||
Output: ['src/main.py:get_document']" | ||
] | ||
|
||
SCRIPT: | ||
``` | ||
print(graph.nodes["src/main.py:get_document"]["document"]) | ||
``` | ||
-------------------------------------------------------------------------------- | ||
USER: "What is does get_document do?" | ||
CONVERSATION: [ | ||
"Script: print([node for node in graph.nodes if 'get_document' in node])\nOutput: ['get_document']\n" | ||
Output: ['src/main.py:get_document']", | ||
"Script: print(graph.nodes['src/main.py:get_document']['document']) | ||
Output: 'def get_document(...): ...'\n" | ||
] | ||
|
||
SCRIPT: | ||
``` | ||
answer("The get_document function in `src/main.py` returns the content of a file by...") | ||
``` |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using
exec
to run scripts can be dangerous as it can execute arbitrary code. Consider using a more secure method to evaluate the script, such as using a restricted execution environment or validating the script more thoroughly before execution.