amd-gaia 0.14.3__py3-none-any.whl → 0.15.1__py3-none-any.whl
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.
- {amd_gaia-0.14.3.dist-info → amd_gaia-0.15.1.dist-info}/METADATA +223 -223
- amd_gaia-0.15.1.dist-info/RECORD +178 -0
- {amd_gaia-0.14.3.dist-info → amd_gaia-0.15.1.dist-info}/entry_points.txt +1 -0
- {amd_gaia-0.14.3.dist-info → amd_gaia-0.15.1.dist-info}/licenses/LICENSE.md +20 -20
- gaia/__init__.py +29 -29
- gaia/agents/__init__.py +19 -19
- gaia/agents/base/__init__.py +9 -9
- gaia/agents/base/agent.py +2177 -2177
- gaia/agents/base/api_agent.py +120 -120
- gaia/agents/base/console.py +1841 -1841
- gaia/agents/base/errors.py +237 -237
- gaia/agents/base/mcp_agent.py +86 -86
- gaia/agents/base/tools.py +83 -83
- gaia/agents/blender/agent.py +556 -556
- gaia/agents/blender/agent_simple.py +133 -135
- gaia/agents/blender/app.py +211 -211
- gaia/agents/blender/app_simple.py +41 -41
- gaia/agents/blender/core/__init__.py +16 -16
- gaia/agents/blender/core/materials.py +506 -506
- gaia/agents/blender/core/objects.py +316 -316
- gaia/agents/blender/core/rendering.py +225 -225
- gaia/agents/blender/core/scene.py +220 -220
- gaia/agents/blender/core/view.py +146 -146
- gaia/agents/chat/__init__.py +9 -9
- gaia/agents/chat/agent.py +835 -835
- gaia/agents/chat/app.py +1058 -1058
- gaia/agents/chat/session.py +508 -508
- gaia/agents/chat/tools/__init__.py +15 -15
- gaia/agents/chat/tools/file_tools.py +96 -96
- gaia/agents/chat/tools/rag_tools.py +1729 -1729
- gaia/agents/chat/tools/shell_tools.py +436 -436
- gaia/agents/code/__init__.py +7 -7
- gaia/agents/code/agent.py +549 -549
- gaia/agents/code/cli.py +377 -0
- gaia/agents/code/models.py +135 -135
- gaia/agents/code/orchestration/__init__.py +24 -24
- gaia/agents/code/orchestration/checklist_executor.py +1763 -1763
- gaia/agents/code/orchestration/checklist_generator.py +713 -713
- gaia/agents/code/orchestration/factories/__init__.py +9 -9
- gaia/agents/code/orchestration/factories/base.py +63 -63
- gaia/agents/code/orchestration/factories/nextjs_factory.py +118 -118
- gaia/agents/code/orchestration/factories/python_factory.py +106 -106
- gaia/agents/code/orchestration/orchestrator.py +841 -841
- gaia/agents/code/orchestration/project_analyzer.py +391 -391
- gaia/agents/code/orchestration/steps/__init__.py +67 -67
- gaia/agents/code/orchestration/steps/base.py +188 -188
- gaia/agents/code/orchestration/steps/error_handler.py +314 -314
- gaia/agents/code/orchestration/steps/nextjs.py +828 -828
- gaia/agents/code/orchestration/steps/python.py +307 -307
- gaia/agents/code/orchestration/template_catalog.py +469 -469
- gaia/agents/code/orchestration/workflows/__init__.py +14 -14
- gaia/agents/code/orchestration/workflows/base.py +80 -80
- gaia/agents/code/orchestration/workflows/nextjs.py +186 -186
- gaia/agents/code/orchestration/workflows/python.py +94 -94
- gaia/agents/code/prompts/__init__.py +11 -11
- gaia/agents/code/prompts/base_prompt.py +77 -77
- gaia/agents/code/prompts/code_patterns.py +2036 -2036
- gaia/agents/code/prompts/nextjs_prompt.py +40 -40
- gaia/agents/code/prompts/python_prompt.py +109 -109
- gaia/agents/code/schema_inference.py +365 -365
- gaia/agents/code/system_prompt.py +41 -41
- gaia/agents/code/tools/__init__.py +42 -42
- gaia/agents/code/tools/cli_tools.py +1138 -1138
- gaia/agents/code/tools/code_formatting.py +319 -319
- gaia/agents/code/tools/code_tools.py +769 -769
- gaia/agents/code/tools/error_fixing.py +1347 -1347
- gaia/agents/code/tools/external_tools.py +180 -180
- gaia/agents/code/tools/file_io.py +845 -845
- gaia/agents/code/tools/prisma_tools.py +190 -190
- gaia/agents/code/tools/project_management.py +1016 -1016
- gaia/agents/code/tools/testing.py +321 -321
- gaia/agents/code/tools/typescript_tools.py +122 -122
- gaia/agents/code/tools/validation_parsing.py +461 -461
- gaia/agents/code/tools/validation_tools.py +806 -806
- gaia/agents/code/tools/web_dev_tools.py +1758 -1758
- gaia/agents/code/validators/__init__.py +16 -16
- gaia/agents/code/validators/antipattern_checker.py +241 -241
- gaia/agents/code/validators/ast_analyzer.py +197 -197
- gaia/agents/code/validators/requirements_validator.py +145 -145
- gaia/agents/code/validators/syntax_validator.py +171 -171
- gaia/agents/docker/__init__.py +7 -7
- gaia/agents/docker/agent.py +642 -642
- gaia/agents/emr/__init__.py +8 -8
- gaia/agents/emr/agent.py +1506 -1506
- gaia/agents/emr/cli.py +1322 -1322
- gaia/agents/emr/constants.py +475 -475
- gaia/agents/emr/dashboard/__init__.py +4 -4
- gaia/agents/emr/dashboard/server.py +1974 -1974
- gaia/agents/jira/__init__.py +11 -11
- gaia/agents/jira/agent.py +894 -894
- gaia/agents/jira/jql_templates.py +299 -299
- gaia/agents/routing/__init__.py +7 -7
- gaia/agents/routing/agent.py +567 -570
- gaia/agents/routing/system_prompt.py +75 -75
- gaia/agents/summarize/__init__.py +11 -0
- gaia/agents/summarize/agent.py +885 -0
- gaia/agents/summarize/prompts.py +129 -0
- gaia/api/__init__.py +23 -23
- gaia/api/agent_registry.py +238 -238
- gaia/api/app.py +305 -305
- gaia/api/openai_server.py +575 -575
- gaia/api/schemas.py +186 -186
- gaia/api/sse_handler.py +373 -373
- gaia/apps/__init__.py +4 -4
- gaia/apps/llm/__init__.py +6 -6
- gaia/apps/llm/app.py +173 -169
- gaia/apps/summarize/app.py +116 -633
- gaia/apps/summarize/html_viewer.py +133 -133
- gaia/apps/summarize/pdf_formatter.py +284 -284
- gaia/audio/__init__.py +2 -2
- gaia/audio/audio_client.py +439 -439
- gaia/audio/audio_recorder.py +269 -269
- gaia/audio/kokoro_tts.py +599 -599
- gaia/audio/whisper_asr.py +432 -432
- gaia/chat/__init__.py +16 -16
- gaia/chat/app.py +430 -430
- gaia/chat/prompts.py +522 -522
- gaia/chat/sdk.py +1228 -1225
- gaia/cli.py +5481 -5621
- gaia/database/__init__.py +10 -10
- gaia/database/agent.py +176 -176
- gaia/database/mixin.py +290 -290
- gaia/database/testing.py +64 -64
- gaia/eval/batch_experiment.py +2332 -2332
- gaia/eval/claude.py +542 -542
- gaia/eval/config.py +37 -37
- gaia/eval/email_generator.py +512 -512
- gaia/eval/eval.py +3179 -3179
- gaia/eval/groundtruth.py +1130 -1130
- gaia/eval/transcript_generator.py +582 -582
- gaia/eval/webapp/README.md +167 -167
- gaia/eval/webapp/package-lock.json +875 -875
- gaia/eval/webapp/package.json +20 -20
- gaia/eval/webapp/public/app.js +3402 -3402
- gaia/eval/webapp/public/index.html +87 -87
- gaia/eval/webapp/public/styles.css +3661 -3661
- gaia/eval/webapp/server.js +415 -415
- gaia/eval/webapp/test-setup.js +72 -72
- gaia/llm/__init__.py +9 -2
- gaia/llm/base_client.py +60 -0
- gaia/llm/exceptions.py +12 -0
- gaia/llm/factory.py +70 -0
- gaia/llm/lemonade_client.py +3236 -3221
- gaia/llm/lemonade_manager.py +294 -294
- gaia/llm/providers/__init__.py +9 -0
- gaia/llm/providers/claude.py +108 -0
- gaia/llm/providers/lemonade.py +120 -0
- gaia/llm/providers/openai_provider.py +79 -0
- gaia/llm/vlm_client.py +382 -382
- gaia/logger.py +189 -189
- gaia/mcp/agent_mcp_server.py +245 -245
- gaia/mcp/blender_mcp_client.py +138 -138
- gaia/mcp/blender_mcp_server.py +648 -648
- gaia/mcp/context7_cache.py +332 -332
- gaia/mcp/external_services.py +518 -518
- gaia/mcp/mcp_bridge.py +811 -550
- gaia/mcp/servers/__init__.py +6 -6
- gaia/mcp/servers/docker_mcp.py +83 -83
- gaia/perf_analysis.py +361 -0
- gaia/rag/__init__.py +10 -10
- gaia/rag/app.py +293 -293
- gaia/rag/demo.py +304 -304
- gaia/rag/pdf_utils.py +235 -235
- gaia/rag/sdk.py +2194 -2194
- gaia/security.py +163 -163
- gaia/talk/app.py +289 -289
- gaia/talk/sdk.py +538 -538
- gaia/testing/__init__.py +87 -87
- gaia/testing/assertions.py +330 -330
- gaia/testing/fixtures.py +333 -333
- gaia/testing/mocks.py +493 -493
- gaia/util.py +46 -46
- gaia/utils/__init__.py +33 -33
- gaia/utils/file_watcher.py +675 -675
- gaia/utils/parsing.py +223 -223
- gaia/version.py +100 -100
- amd_gaia-0.14.3.dist-info/RECORD +0 -168
- gaia/agents/code/app.py +0 -266
- gaia/llm/llm_client.py +0 -729
- {amd_gaia-0.14.3.dist-info → amd_gaia-0.15.1.dist-info}/WHEEL +0 -0
- {amd_gaia-0.14.3.dist-info → amd_gaia-0.15.1.dist-info}/top_level.txt +0 -0
|
@@ -1,197 +1,197 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
|
2
|
-
# Copyright(C) 2024-2025 Advanced Micro Devices, Inc. All rights reserved.
|
|
3
|
-
# SPDX-License-Identifier: MIT
|
|
4
|
-
"""AST parsing and analysis for Python code."""
|
|
5
|
-
|
|
6
|
-
import ast
|
|
7
|
-
from typing import List, Optional
|
|
8
|
-
|
|
9
|
-
from ..models import CodeSymbol, ParsedCode
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class ASTAnalyzer:
|
|
13
|
-
"""Analyzes Python code using Abstract Syntax Trees."""
|
|
14
|
-
|
|
15
|
-
def parse_code(self, code: str) -> ParsedCode:
|
|
16
|
-
"""Parse Python code using AST.
|
|
17
|
-
|
|
18
|
-
Args:
|
|
19
|
-
code: Python source code
|
|
20
|
-
|
|
21
|
-
Returns:
|
|
22
|
-
ParsedCode object with parsing results
|
|
23
|
-
"""
|
|
24
|
-
result = ParsedCode()
|
|
25
|
-
result.symbols = []
|
|
26
|
-
result.imports = []
|
|
27
|
-
result.errors = []
|
|
28
|
-
|
|
29
|
-
try:
|
|
30
|
-
# Parse the code into an AST
|
|
31
|
-
tree = ast.parse(code)
|
|
32
|
-
result.ast_tree = tree
|
|
33
|
-
result.is_valid = True
|
|
34
|
-
|
|
35
|
-
# Extract symbols from the AST
|
|
36
|
-
for node in ast.walk(tree):
|
|
37
|
-
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
38
|
-
# Extract function information
|
|
39
|
-
signature = self._get_function_signature(node)
|
|
40
|
-
docstring = ast.get_docstring(node)
|
|
41
|
-
result.symbols.append(
|
|
42
|
-
CodeSymbol(
|
|
43
|
-
name=node.name,
|
|
44
|
-
type="function",
|
|
45
|
-
line=node.lineno,
|
|
46
|
-
signature=signature,
|
|
47
|
-
docstring=docstring,
|
|
48
|
-
)
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
elif isinstance(node, ast.ClassDef):
|
|
52
|
-
# Extract class information
|
|
53
|
-
docstring = ast.get_docstring(node)
|
|
54
|
-
result.symbols.append(
|
|
55
|
-
CodeSymbol(
|
|
56
|
-
name=node.name,
|
|
57
|
-
type="class",
|
|
58
|
-
line=node.lineno,
|
|
59
|
-
docstring=docstring,
|
|
60
|
-
)
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
elif isinstance(node, ast.Import):
|
|
64
|
-
# Extract import statements
|
|
65
|
-
for alias in node.names:
|
|
66
|
-
import_name = alias.asname if alias.asname else alias.name
|
|
67
|
-
result.imports.append(f"import {alias.name}")
|
|
68
|
-
result.symbols.append(
|
|
69
|
-
CodeSymbol(
|
|
70
|
-
name=import_name, type="import", line=node.lineno
|
|
71
|
-
)
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
elif isinstance(node, ast.ImportFrom):
|
|
75
|
-
# Extract from...import statements
|
|
76
|
-
module = node.module if node.module else ""
|
|
77
|
-
for alias in node.names:
|
|
78
|
-
import_name = alias.asname if alias.asname else alias.name
|
|
79
|
-
result.imports.append(f"from {module} import {alias.name}")
|
|
80
|
-
result.symbols.append(
|
|
81
|
-
CodeSymbol(
|
|
82
|
-
name=import_name, type="import", line=node.lineno
|
|
83
|
-
)
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
elif isinstance(node, ast.Assign):
|
|
87
|
-
# Extract module-level variable assignments
|
|
88
|
-
for target in node.targets:
|
|
89
|
-
if isinstance(target, ast.Name) and isinstance(
|
|
90
|
-
target.ctx, ast.Store
|
|
91
|
-
):
|
|
92
|
-
# Check if this is at module level (col_offset == 0)
|
|
93
|
-
if hasattr(node, "col_offset") and node.col_offset == 0:
|
|
94
|
-
result.symbols.append(
|
|
95
|
-
CodeSymbol(
|
|
96
|
-
name=target.id,
|
|
97
|
-
type="variable",
|
|
98
|
-
line=node.lineno,
|
|
99
|
-
)
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
except SyntaxError as e:
|
|
103
|
-
result.is_valid = False
|
|
104
|
-
result.errors.append(f"Syntax error at line {e.lineno}: {e.msg}")
|
|
105
|
-
except Exception as e:
|
|
106
|
-
result.is_valid = False
|
|
107
|
-
result.errors.append(f"Parse error: {str(e)}")
|
|
108
|
-
|
|
109
|
-
return result
|
|
110
|
-
|
|
111
|
-
def _get_function_signature(
|
|
112
|
-
self, node: ast.FunctionDef | ast.AsyncFunctionDef
|
|
113
|
-
) -> str:
|
|
114
|
-
"""Extract function signature from AST node.
|
|
115
|
-
|
|
116
|
-
Args:
|
|
117
|
-
node: AST FunctionDef or AsyncFunctionDef node
|
|
118
|
-
|
|
119
|
-
Returns:
|
|
120
|
-
Function signature as string
|
|
121
|
-
"""
|
|
122
|
-
params = []
|
|
123
|
-
|
|
124
|
-
# Regular arguments
|
|
125
|
-
for arg in node.args.args:
|
|
126
|
-
param = arg.arg
|
|
127
|
-
if arg.annotation:
|
|
128
|
-
param += f": {ast.unparse(arg.annotation)}"
|
|
129
|
-
params.append(param)
|
|
130
|
-
|
|
131
|
-
# *args
|
|
132
|
-
if node.args.vararg:
|
|
133
|
-
param = f"*{node.args.vararg.arg}"
|
|
134
|
-
if node.args.vararg.annotation:
|
|
135
|
-
param += f": {ast.unparse(node.args.vararg.annotation)}"
|
|
136
|
-
params.append(param)
|
|
137
|
-
|
|
138
|
-
# **kwargs
|
|
139
|
-
if node.args.kwarg:
|
|
140
|
-
param = f"**{node.args.kwarg.arg}"
|
|
141
|
-
if node.args.kwarg.annotation:
|
|
142
|
-
param += f": {ast.unparse(node.args.kwarg.annotation)}"
|
|
143
|
-
params.append(param)
|
|
144
|
-
|
|
145
|
-
signature = f"{node.name}({', '.join(params)})"
|
|
146
|
-
|
|
147
|
-
# Add return type if present
|
|
148
|
-
if node.returns:
|
|
149
|
-
signature += f" -> {ast.unparse(node.returns)}"
|
|
150
|
-
|
|
151
|
-
return signature
|
|
152
|
-
|
|
153
|
-
def extract_functions(
|
|
154
|
-
self, tree: ast.Module
|
|
155
|
-
) -> List[ast.FunctionDef | ast.AsyncFunctionDef]:
|
|
156
|
-
"""Extract all function definitions from an AST.
|
|
157
|
-
|
|
158
|
-
Args:
|
|
159
|
-
tree: AST Module to analyze
|
|
160
|
-
|
|
161
|
-
Returns:
|
|
162
|
-
List of FunctionDef and AsyncFunctionDef nodes
|
|
163
|
-
"""
|
|
164
|
-
functions = []
|
|
165
|
-
for node in ast.walk(tree):
|
|
166
|
-
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
167
|
-
functions.append(node)
|
|
168
|
-
return functions
|
|
169
|
-
|
|
170
|
-
def extract_classes(self, tree: ast.Module) -> List[ast.ClassDef]:
|
|
171
|
-
"""Extract all class definitions from an AST.
|
|
172
|
-
|
|
173
|
-
Args:
|
|
174
|
-
tree: AST Module to analyze
|
|
175
|
-
|
|
176
|
-
Returns:
|
|
177
|
-
List of ClassDef nodes
|
|
178
|
-
"""
|
|
179
|
-
classes = []
|
|
180
|
-
for node in ast.walk(tree):
|
|
181
|
-
if isinstance(node, ast.ClassDef):
|
|
182
|
-
classes.append(node)
|
|
183
|
-
return classes
|
|
184
|
-
|
|
185
|
-
def get_docstring(
|
|
186
|
-
self,
|
|
187
|
-
node: ast.FunctionDef | ast.AsyncFunctionDef | ast.ClassDef | ast.Module,
|
|
188
|
-
) -> Optional[str]:
|
|
189
|
-
"""Extract docstring from an AST node.
|
|
190
|
-
|
|
191
|
-
Args:
|
|
192
|
-
node: AST node to extract docstring from
|
|
193
|
-
|
|
194
|
-
Returns:
|
|
195
|
-
Docstring text or None
|
|
196
|
-
"""
|
|
197
|
-
return ast.get_docstring(node)
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# Copyright(C) 2024-2025 Advanced Micro Devices, Inc. All rights reserved.
|
|
3
|
+
# SPDX-License-Identifier: MIT
|
|
4
|
+
"""AST parsing and analysis for Python code."""
|
|
5
|
+
|
|
6
|
+
import ast
|
|
7
|
+
from typing import List, Optional
|
|
8
|
+
|
|
9
|
+
from ..models import CodeSymbol, ParsedCode
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ASTAnalyzer:
|
|
13
|
+
"""Analyzes Python code using Abstract Syntax Trees."""
|
|
14
|
+
|
|
15
|
+
def parse_code(self, code: str) -> ParsedCode:
|
|
16
|
+
"""Parse Python code using AST.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
code: Python source code
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
ParsedCode object with parsing results
|
|
23
|
+
"""
|
|
24
|
+
result = ParsedCode()
|
|
25
|
+
result.symbols = []
|
|
26
|
+
result.imports = []
|
|
27
|
+
result.errors = []
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
# Parse the code into an AST
|
|
31
|
+
tree = ast.parse(code)
|
|
32
|
+
result.ast_tree = tree
|
|
33
|
+
result.is_valid = True
|
|
34
|
+
|
|
35
|
+
# Extract symbols from the AST
|
|
36
|
+
for node in ast.walk(tree):
|
|
37
|
+
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
38
|
+
# Extract function information
|
|
39
|
+
signature = self._get_function_signature(node)
|
|
40
|
+
docstring = ast.get_docstring(node)
|
|
41
|
+
result.symbols.append(
|
|
42
|
+
CodeSymbol(
|
|
43
|
+
name=node.name,
|
|
44
|
+
type="function",
|
|
45
|
+
line=node.lineno,
|
|
46
|
+
signature=signature,
|
|
47
|
+
docstring=docstring,
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
elif isinstance(node, ast.ClassDef):
|
|
52
|
+
# Extract class information
|
|
53
|
+
docstring = ast.get_docstring(node)
|
|
54
|
+
result.symbols.append(
|
|
55
|
+
CodeSymbol(
|
|
56
|
+
name=node.name,
|
|
57
|
+
type="class",
|
|
58
|
+
line=node.lineno,
|
|
59
|
+
docstring=docstring,
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
elif isinstance(node, ast.Import):
|
|
64
|
+
# Extract import statements
|
|
65
|
+
for alias in node.names:
|
|
66
|
+
import_name = alias.asname if alias.asname else alias.name
|
|
67
|
+
result.imports.append(f"import {alias.name}")
|
|
68
|
+
result.symbols.append(
|
|
69
|
+
CodeSymbol(
|
|
70
|
+
name=import_name, type="import", line=node.lineno
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
elif isinstance(node, ast.ImportFrom):
|
|
75
|
+
# Extract from...import statements
|
|
76
|
+
module = node.module if node.module else ""
|
|
77
|
+
for alias in node.names:
|
|
78
|
+
import_name = alias.asname if alias.asname else alias.name
|
|
79
|
+
result.imports.append(f"from {module} import {alias.name}")
|
|
80
|
+
result.symbols.append(
|
|
81
|
+
CodeSymbol(
|
|
82
|
+
name=import_name, type="import", line=node.lineno
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
elif isinstance(node, ast.Assign):
|
|
87
|
+
# Extract module-level variable assignments
|
|
88
|
+
for target in node.targets:
|
|
89
|
+
if isinstance(target, ast.Name) and isinstance(
|
|
90
|
+
target.ctx, ast.Store
|
|
91
|
+
):
|
|
92
|
+
# Check if this is at module level (col_offset == 0)
|
|
93
|
+
if hasattr(node, "col_offset") and node.col_offset == 0:
|
|
94
|
+
result.symbols.append(
|
|
95
|
+
CodeSymbol(
|
|
96
|
+
name=target.id,
|
|
97
|
+
type="variable",
|
|
98
|
+
line=node.lineno,
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
except SyntaxError as e:
|
|
103
|
+
result.is_valid = False
|
|
104
|
+
result.errors.append(f"Syntax error at line {e.lineno}: {e.msg}")
|
|
105
|
+
except Exception as e:
|
|
106
|
+
result.is_valid = False
|
|
107
|
+
result.errors.append(f"Parse error: {str(e)}")
|
|
108
|
+
|
|
109
|
+
return result
|
|
110
|
+
|
|
111
|
+
def _get_function_signature(
|
|
112
|
+
self, node: ast.FunctionDef | ast.AsyncFunctionDef
|
|
113
|
+
) -> str:
|
|
114
|
+
"""Extract function signature from AST node.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
node: AST FunctionDef or AsyncFunctionDef node
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Function signature as string
|
|
121
|
+
"""
|
|
122
|
+
params = []
|
|
123
|
+
|
|
124
|
+
# Regular arguments
|
|
125
|
+
for arg in node.args.args:
|
|
126
|
+
param = arg.arg
|
|
127
|
+
if arg.annotation:
|
|
128
|
+
param += f": {ast.unparse(arg.annotation)}"
|
|
129
|
+
params.append(param)
|
|
130
|
+
|
|
131
|
+
# *args
|
|
132
|
+
if node.args.vararg:
|
|
133
|
+
param = f"*{node.args.vararg.arg}"
|
|
134
|
+
if node.args.vararg.annotation:
|
|
135
|
+
param += f": {ast.unparse(node.args.vararg.annotation)}"
|
|
136
|
+
params.append(param)
|
|
137
|
+
|
|
138
|
+
# **kwargs
|
|
139
|
+
if node.args.kwarg:
|
|
140
|
+
param = f"**{node.args.kwarg.arg}"
|
|
141
|
+
if node.args.kwarg.annotation:
|
|
142
|
+
param += f": {ast.unparse(node.args.kwarg.annotation)}"
|
|
143
|
+
params.append(param)
|
|
144
|
+
|
|
145
|
+
signature = f"{node.name}({', '.join(params)})"
|
|
146
|
+
|
|
147
|
+
# Add return type if present
|
|
148
|
+
if node.returns:
|
|
149
|
+
signature += f" -> {ast.unparse(node.returns)}"
|
|
150
|
+
|
|
151
|
+
return signature
|
|
152
|
+
|
|
153
|
+
def extract_functions(
|
|
154
|
+
self, tree: ast.Module
|
|
155
|
+
) -> List[ast.FunctionDef | ast.AsyncFunctionDef]:
|
|
156
|
+
"""Extract all function definitions from an AST.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
tree: AST Module to analyze
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
List of FunctionDef and AsyncFunctionDef nodes
|
|
163
|
+
"""
|
|
164
|
+
functions = []
|
|
165
|
+
for node in ast.walk(tree):
|
|
166
|
+
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
167
|
+
functions.append(node)
|
|
168
|
+
return functions
|
|
169
|
+
|
|
170
|
+
def extract_classes(self, tree: ast.Module) -> List[ast.ClassDef]:
|
|
171
|
+
"""Extract all class definitions from an AST.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
tree: AST Module to analyze
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
List of ClassDef nodes
|
|
178
|
+
"""
|
|
179
|
+
classes = []
|
|
180
|
+
for node in ast.walk(tree):
|
|
181
|
+
if isinstance(node, ast.ClassDef):
|
|
182
|
+
classes.append(node)
|
|
183
|
+
return classes
|
|
184
|
+
|
|
185
|
+
def get_docstring(
|
|
186
|
+
self,
|
|
187
|
+
node: ast.FunctionDef | ast.AsyncFunctionDef | ast.ClassDef | ast.Module,
|
|
188
|
+
) -> Optional[str]:
|
|
189
|
+
"""Extract docstring from an AST node.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
node: AST node to extract docstring from
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
Docstring text or None
|
|
196
|
+
"""
|
|
197
|
+
return ast.get_docstring(node)
|