Skip to content

Commit

Permalink
metadata handle
Browse files Browse the repository at this point in the history
  • Loading branch information
vysakh0 committed Jul 28, 2024
1 parent 3000f3b commit bd6154d
Show file tree
Hide file tree
Showing 10 changed files with 205 additions and 101 deletions.
69 changes: 54 additions & 15 deletions src/drd/cli/query/dynamic_command_handler.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import traceback
import click
from ...api.main import call_dravid_api
import xml.etree.ElementTree as ET
from ...utils import print_error, print_success, print_info, print_step, print_debug
from ...metadata.common_utils import generate_file_description
from ...prompts.error_resolution_prompt import get_error_resolution_prompt
Expand Down Expand Up @@ -75,6 +76,8 @@ def handle_file_operation(cmd, executor, metadata_manager):
elif operation_performed:
print_success(
f"Successfully performed {cmd['operation']} on file: {cmd['filename']}")
if cmd['operation'] in ['CREATE', 'UPDATE']:
update_file_metadata(cmd, metadata_manager, executor)
return "Success"
else:
raise Exception(
Expand All @@ -94,21 +97,57 @@ def handle_metadata_operation(cmd, metadata_manager):


def update_file_metadata(cmd, metadata_manager, executor):
project_context = metadata_manager.get_project_context()
folder_structure = executor.get_folder_structure()
file_type, description, exports = generate_file_description(
cmd['filename'],
cmd.get('content', ''),
project_context,
folder_structure
)
metadata_manager.update_file_metadata(
cmd['filename'],
file_type,
cmd.get('content', ''),
description,
exports
)
file_info = metadata_manager.analyze_file(cmd['filename'])
if file_info:
metadata_manager.update_file_metadata(
file_info['path'],
file_info['type'],
cmd.get('content', ''),
file_info['summary'],
file_info['exports'],
file_info['imports']
)

# Handle dependencies from the XML response
handle_dependencies(file_info, metadata_manager)


def handle_dependencies(file_info, metadata_manager):
if 'xml_response' in file_info:
try:
root = ET.fromstring(file_info['xml_response'])
dependencies = root.find('.//external_dependencies')
if dependencies is not None:
for dep in dependencies.findall('dependency'):
dependency_info = dep.text.strip()
metadata_manager.add_external_dependency(dependency_info)
print_info(
f"Added {len(dependencies)} dependencies to the project metadata.")

# Handle other metadata updates
update_project_info(root, metadata_manager)
update_dev_server_info(root, metadata_manager)
except ET.ParseError:
print_error("Failed to parse XML response for dependencies")


def update_project_info(root, metadata_manager):
project_info = root.find('.//project_info')
if project_info is not None:
for field in ['name', 'version', 'description']:
element = project_info.find(field)
if element is not None and element.text:
metadata_manager.metadata['project_info'][field] = element.text.strip(
)


def update_dev_server_info(root, metadata_manager):
dev_server = root.find('.//dev_server')
if dev_server is not None:
start_command = dev_server.find('start_command')
if start_command is not None and start_command.text:
metadata_manager.metadata['dev_server']['start_command'] = start_command.text.strip(
)


def handle_error_with_dravid(error, cmd, executor, metadata_manager, depth=0, previous_context="", debug=False):
Expand Down
25 changes: 5 additions & 20 deletions src/drd/cli/query/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ def execute_dravid_command(query, image_path, debug, instruction_prompt, warn=No

full_query = construct_full_query(
query, executor, project_context, files_info, reference_files)
print(full_query, "full query")

print_info("💡 Preparing to send query to LLM...", indent=2)
if image_path:
Expand Down Expand Up @@ -80,9 +79,7 @@ def execute_dravid_command(query, image_path, debug, instruction_prompt, warn=No
success, step_completed, error_message, all_outputs = execute_commands(
commands, executor, metadata_manager, debug=debug)

print("no scucess", success)
if not success:
print("called")
print_error(
f"Failed to execute command at step {step_completed}.")
print_error(f"Error message: {error_message}")
Expand Down Expand Up @@ -111,7 +108,6 @@ def execute_dravid_command(query, image_path, debug, instruction_prompt, warn=No

def construct_full_query(query, executor, project_context, files_info=None, reference_files=None):
is_empty = is_directory_empty(executor.current_dir)

if is_empty:
print_info(
"Current directory is empty. Will create a new project.", indent=2)
Expand All @@ -123,41 +119,32 @@ def construct_full_query(query, executor, project_context, files_info=None, refe
else:
print_info(
"Constructing query with project context and file information.", indent=2)

project_guidelines = fetch_project_guidelines(executor.current_dir)

full_query = f"{project_context}\n\n"
full_query += f"Project Guidelines:\n{project_guidelines}\n\n"

if files_info:
if files_info['file_contents_to_load']:
if files_info and isinstance(files_info, dict):
if 'file_contents_to_load' in files_info:
file_contents = {}
for file in files_info['file_contents_to_load']:
content = get_file_content(file)
if content:
file_contents[file] = content
print_info(f" - Read content of {file}", indent=4)

file_context = "\n".join(
[f"Current content of {file}:\n{content}" for file, content in file_contents.items()])
full_query += f"Current file contents:\n{file_context}\n\n"

if files_info['dependencies']:
if 'dependencies' in files_info:
dependency_context = "\n".join(
[f"Dependency {dep['file']} exports: {', '.join(dep['imports'])}" for dep in files_info['dependencies']])
full_query += f"Dependencies:\n{dependency_context}\n\n"

if files_info['new_files']:
if 'new_files' in files_info:
new_files_context = "\n".join(
[f"New file to create: {new_file['file']}" for new_file in files_info['new_files']])
full_query += f"New files to create:\n{new_files_context}\n\n"

if files_info['main_file']:
if 'main_file' in files_info:
full_query += f"Main file to modify: {files_info['main_file']}\n\n"

full_query += "Current directory is not empty.\n\n"
full_query += f"User query: {query}"

if reference_files:
print_info("📄 Reading reference file contents...", indent=2)
reference_contents = {}
Expand All @@ -166,9 +153,7 @@ def construct_full_query(query, executor, project_context, files_info=None, refe
if content:
reference_contents[file] = content
print_info(f" - Read content of {file}", indent=4)

reference_context = "\n\n".join(
[f"Reference file {file}:\n{content}" for file, content in reference_contents.items()])
full_query += f"\n\nReference files:\n{reference_context}"

return full_query
34 changes: 33 additions & 1 deletion src/drd/metadata/project_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ async def analyze_file(self, file_path):
file_info = {
"path": rel_path,
"type": metadata.find('type').text,
"summary": metadata.find('description').text,
"summary": metadata.find('summary').text,
"exports": metadata.find('exports').text.split(',') if metadata.find('exports').text != 'None' else [],
"imports": metadata.find('imports').text.split(',') if metadata.find('imports').text != 'None' else []
}
Expand Down Expand Up @@ -250,3 +250,35 @@ def update_file_metadata(self, filename, file_type, content, description=None, e
'imports': imports or []
})
self.save_metadata()

def update_metadata_from_file(self):
if os.path.exists(self.metadata_file):
with open(self.metadata_file, 'r') as f:
content = f.read()
try:
new_metadata = json.loads(content)
# Update dev server info if present
if 'dev_server' in new_metadata:
self.metadata['dev_server'] = new_metadata['dev_server']
# Update other metadata fields
for key, value in new_metadata.items():
if key != 'files': # We'll handle files separately
self.metadata[key] = value
# Update file metadata
if 'files' in new_metadata:
for file_entry in new_metadata['files']:
filename = file_entry['filename']
file_type = file_entry.get(
'type', filename.split('.')[-1])
file_content = file_entry.get('content', '')
description = file_entry.get('description', '')
exports = file_entry.get('exports', [])
imports = file_entry.get('imports', [])
self.update_file_metadata(
filename, file_type, file_content, description, exports, imports)
self.save_metadata()
return True
except json.JSONDecodeError:
print(f"Error: Invalid JSON content in {self.metadata_file}")
return False
return False
18 changes: 10 additions & 8 deletions src/drd/metadata/rate_limit_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,26 @@ async def process_single_file(filename, content, project_context, folder_structu
async with rate_limiter.semaphore:
await rate_limiter.acquire()
response = await to_thread(call_dravid_api_with_pagination, metadata_query, include_context=True)

root = extract_and_parse_xml(response)
type_elem = root.find('.//type')
desc_elem = root.find('.//description')
summary_elem = root.find('.//summary')
exports_elem = root.find('.//exports')

imports_elem = root.find('.//imports') # Added imports_elem
file_type = type_elem.text.strip(
) if type_elem is not None and type_elem.text else "unknown"
description = desc_elem.text.strip(
) if desc_elem is not None and desc_elem.text else "No description available"
summary = summary_elem.text.strip(
) if summary_elem is not None and summary_elem.text else "No summary available"
exports = exports_elem.text.strip(
) if exports_elem is not None and exports_elem.text else ""

imports = imports_elem.text.strip(
) if imports_elem is not None and imports_elem.text else "" # Added imports
print_success(f"Processed: {filename}")
return filename, file_type, description, exports
# Added imports to return tuple
return filename, file_type, summary, exports, imports
except Exception as e:
print_error(f"Error processing {filename}: {e}")
return filename, "unknown", f"Error: {e}", ""
# Added empty string for imports in error case
return filename, "unknown", f"Error: {e}", "", ""


async def process_files(files, project_context, folder_structure):
Expand Down
23 changes: 9 additions & 14 deletions src/drd/metadata/updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,26 +70,21 @@ async def update_metadata_with_dravid_async(meta_description, current_dir):
)
print_success(
f"Updated metadata for file: {found_filename}")

# Handle external dependencies
metadata = file.find('metadata')
if metadata is not None:
external_deps = metadata.find('external_dependencies')
if external_deps is not None:
for dep in external_deps.findall('dependency'):
metadata_manager.add_external_dependency(
dep.text.strip())
else:
print_warning(f"Could not analyze file: {found_filename}")

except Exception as e:
print_error(f"Error processing {found_filename}: {str(e)}")

# After processing all files, update the environment info
all_languages = set(file['type'] for file in metadata_manager.metadata['key_files']
if file['type'] not in ['binary', 'unknown'])
if all_languages:
primary_language = max(all_languages, key=lambda x: sum(
1 for file in metadata_manager.metadata['key_files'] if file['type'] == x))
other_languages = list(all_languages - {primary_language})
metadata_manager.update_environment_info(
primary_language=primary_language,
other_languages=other_languages,
primary_framework=metadata_manager.metadata['environment']['primary_framework'],
runtime_version=metadata_manager.metadata['environment']['runtime_version']
)

print_success("Metadata update completed.")
except Exception as e:
print_error(f"Error parsing dravid's response: {str(e)}")
Expand Down
55 changes: 43 additions & 12 deletions src/drd/prompts/file_metada_desc_prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,32 @@ def get_file_metadata_prompt(filename, content, project_context, folder_structur
so it can be used by an AI coding assistant in future for reference.
Based on the file content, project context, and the current folder structure,
please generate appropriate metadata for this file.
please generate appropriate metadata for this file
If this file appears to be a dependency management file (like package.json, requirements.txt, Cargo.toml, etc.),
provide a list of external dependencies.
Guidelines:
1. 'path' should be the full path of the file within the project.
2. 'type' should be the programming language or file type (e.g., "typescript", "python", "json").
3. 'summary' should be a concise description of the file's main purpose.
4. 'exports' should list the exported items with their types (fun: for functions, class: for classes, var: for variables etc).
5. 'imports' should list imports from other project files, including the path and imported item.
6. 'external_dependencies' should list external dependencies for dependency management files if the current file appears to
be deps management file (package.json, requirements.txt, Cargo.toml etc).
7. If there are no exports, imports, or external dependencies, use an empty array [].
8. Ensure all fields are present in the JSON object.
9. If there are no exports, use <exports>None</exports> instead of an empty tag.
10. If there are no imports, use <imports>None</imports> instead of an empty tag.
11. If there are no external dependencies, omit the <external_dependencies> tag entirely.
12. Ensure that all other tags (type, description, file_category, exports, imports) are always present and non-empty.
If it's a code file, provide a summary, list of exports (functions, classes, or variables available for importing),
and a list of imports from other project files.
Respond with an XML structure containing the metadata:
<response>
<metadata>
<type>file_type</type>
<description>Description based on the file's contents, project context, and folder structure</description>
<file_category>code_file or dependency_file</file_category>
<summary>summary based on the file's contents, project context, and folder structure</summary>
<exports>fun:functionName,class:ClassName,var:variableName</exports>
<imports>path/to/file:importedName</imports>
<imports>path/to/file</imports>
<external_dependencies>
<dependency>
<dependency>name1@version1</dependency>
Expand All @@ -36,9 +45,31 @@ def get_file_metadata_prompt(filename, content, project_context, folder_structur
</metadata>
</response>
examples:
<response>
<metadata>
<path>src/components/Layout.tsx</path>
<type>typescript</type>
<summary>Main layout component</summary>
<exports>fun:Layout</exports>
<imports>src/components/Footer</imports>
</metadata>
</response>
<response>
<metadata>
<path>package.json</path>
<type>json</type>
<summary>Node.js project configuration and dependencies</summary>
<exports>None</exports>
<imports>None</imports>
<external_dependencies>
<dependency>[email protected]</dependency>
<dependency>[email protected]</dependency>
<dependency>[email protected]</dependency>
</external_dependencies>
</metadata>
</response>
Respond strictly only with the XML response as it will be used for parsing, no other extra words.
If there are no exports, use <exports>None</exports> instead of an empty tag.
If there are no imports, use <imports>None</imports> instead of an empty tag.
If there are no external dependencies, omit the <external_dependencies> tag entirely.
Ensure that all other tags (type, description, file_category, exports, imports) are always present and non-empty.
"""
Loading

0 comments on commit bd6154d

Please sign in to comment.