syum-af commited on
Commit
60e356c
·
0 Parent(s):

downgraded to python 3.11

Browse files
.gitignore ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+ .env
.python-version ADDED
@@ -0,0 +1 @@
 
 
1
+ 3.10
CLAUDE.md ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Development Practice
2
+
3
+ 1. Act as a pairing partner for the user. Do not commit anything until explicit consent is given
4
+ 2. Use test-driven development (TDD) practice.
5
+ * Write a failing test
6
+ * Implement by writing minimal amount of code to make the test pass
7
+ * Refactor
8
+ 3. Update this document with up-to-date project structures and architecture.
9
+ 4. Run unit tests after every change: `uv run pytest tests/ -v`
10
+
11
+ # Architecture
12
+
13
+ ## Framework
14
+ * Smolagents - abstracts interaction with LLMs (Ollama for local development, OpenAI for production)
15
+ * Use LiteLLMModel wrapper for local development
16
+ * Use OpenAIServerModel for production
17
+ * ToolCallingAgent with FinalAnswerTool for code analysis
18
+ * Gradio - Implements web UI with drag & drop file upload
19
+ * Langfuse - Observability (planned)
20
+
21
+ ## Model Configuration
22
+ * Environment-based configuration via .env file
23
+ * Local development: Ollama with qwen2.5-coder model via LiteLLMModel
24
+ * Production: OpenAI gpt-4o-mini via OpenAIServerModel
25
+ * Configuration variables:
26
+ * ENVIRONMENT (development|production)
27
+ * DEVELOPMENT_MODEL_ID (default: ollama/qwen2.5-coder)
28
+ * PRODUCTION_MODEL_ID (default: gpt-4o-mini)
29
+
30
+ ## Project Structure
31
+ ```
32
+ src/
33
+ ├── agents/
34
+ │ └── code_analyzer.py # CodeAnalyzerAgent for business logic extraction
35
+ └── main.py # CLI interface for development/testing
36
+ tests/
37
+ ├── test_code_analyzer_agent.py # Unit tests for CodeAnalyzerAgent
38
+ └── tictactoe.ads # Sample Ada file for testing
39
+ app.py # Gradio web interface
40
+ requirements.txt # Generated from uv dependencies
41
+ pyproject.toml # Project configuration and dependencies
42
+ .env # Environment configuration
43
+ ```
44
+
45
+ ## Agents
46
+ * **CodeAnalyzerAgent**: Analyzes Ada files to extract business logic
47
+ * Uses dependency injection pattern (model passed as parameter)
48
+ * extract_business_logic() method analyzes Ada code via LLM
49
+ * Returns structured JSON with core algorithms, data structures, business rules
50
+ * Handles JSON parsing errors with fallback responses
51
+
52
+ ## Hosting Platform
53
+ * Hugging Face Spaces with Gradio SDK
54
+
55
+
IDEAS.md ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Project: Ada Conversion Assistant
2
+
3
+ This project would help user modernize legacy Ada project, widely used in the defense industry.
4
+
5
+ Ideally, the user would be able to upload the entire codebase in a zip file and this tool would
6
+
7
+ 1. Describe the overall structure and business logic in the code
8
+ 2. Convert Ada modules into modern programming languagues such as python or typescript
9
+ 3. Download the converted project in a zip file
10
+ 4. Have a chat bot that can answer any question about the project
11
+
12
+ Hosting Platform: Hugging Face
13
+ Fremwork: smolagent
14
+ UI: Gradio
15
+ Tracing: Langfuse
16
+ LLM: OpenAI
README.md ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Ada Assistant
3
+ emoji: 📊
4
+ colorFrom: pink
5
+ colorTo: purple
6
+ sdk: gradio
7
+ sdk_version: 5.33.2
8
+ app_file: app.py
9
+ pinned: false
10
+ ---
11
+
12
+ # Ada Conversion Assistant
13
+
14
+ A modern tool for analyzing legacy Ada codebases and providing conversion recommendations to modern programming languages.
15
+
16
+ ## Features
17
+
18
+ - **Business Logic Analysis**: Extracts core algorithms, data structures, and business rules from Ada code
19
+ - **Conversion Recommendations**: Provides guidance on complexity and modernization approaches
20
+ - **Web Interface**: Drag & drop file upload with Gradio UI
21
+ - **LLM-Powered**: Uses AI models (Ollama for development, OpenAI for production) via smolagents
22
+
23
+ ## Architecture
24
+
25
+ - **Framework**: Smolagents for LLM abstraction, Gradio for UI
26
+ - **Agents**: Multi-agent architecture with specialized CodeAnalyzerAgent
27
+ - **Hosting**: Designed for Hugging Face Spaces deployment
28
+ - **Observability**: Langfuse integration (planned)
29
+
30
+ ## Usage
31
+
32
+ ### Web Interface
33
+ 1. Upload Ada files (.ads, .adb, .ada)
34
+ 2. Click "Analyze Business Logic"
35
+ 3. View extracted business logic and conversion recommendations
36
+
37
+ ### Command Line
38
+ ```bash
39
+ uv run src/main.py
40
+ ```
41
+
42
+ ## Development
43
+
44
+ ### Setup
45
+ ```bash
46
+ # Install dependencies
47
+ uv install
48
+
49
+ # Set environment variables
50
+ cp .env.example .env
51
+ ```
52
+
53
+ ### Configuration
54
+ - `ENVIRONMENT`: "development" or "production"
55
+ - `DEVELOPMENT_MODEL_ID`: Ollama Model for local development (default: ollama/qwen2.5-coder)
56
+ - `PRODUCTION_MODEL_ID`: OpenAI Model for production (default: gpt-4o-mini)
57
+
58
+ ### Testing
59
+ ```bash
60
+ uv run pytest tests/
61
+ ```
62
+
63
+ ## Project Structure
64
+ ```
65
+ src/
66
+ ├── agents/
67
+ │ └── code_analyzer.py # Business logic analysis agent
68
+ └── main.py # CLI interface
69
+ tests/
70
+ ├── test_code_analyzer_agent.py
71
+ └── tictactoe.ads # Test Ada file
72
+ app.py # Gradio web interface
73
+ ```
app.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ from pathlib import Path
4
+ from dotenv import load_dotenv
5
+ from smolagents import LiteLLMModel, OpenAIServerModel
6
+ from src.agents.code_analyzer import CodeAnalyzerAgent
7
+ import json
8
+
9
+ # Load environment variables
10
+ load_dotenv()
11
+
12
+ def analyze_ada_file(file):
13
+ """Analyze uploaded Ada file and extract business logic"""
14
+ if file is None:
15
+ return "No file uploaded"
16
+
17
+ try:
18
+ # Initialize model
19
+ environment = os.getenv("ENVIRONMENT", "development")
20
+ if environment == "production":
21
+ model_id = os.getenv("PRODUCTION_MODEL_ID", "gpt-4o-mini")
22
+ model = OpenAIServerModel(
23
+ model_id=model_id,
24
+ api_key=os.environ["OPENAI_API_KEY"]
25
+ )
26
+ else:
27
+ model_id = os.getenv("DEVELOPMENT_MODEL_ID", "ollama/llama3.2")
28
+ model = LiteLLMModel(model_id=model_id)
29
+
30
+ # Initialize analyzer
31
+ analyzer = CodeAnalyzerAgent(model)
32
+
33
+ # Analyze the uploaded file
34
+ result = analyzer.extract_business_logic(file.name)
35
+
36
+ # Format result for display
37
+ return json.dumps(result, indent=2)
38
+
39
+ except Exception as e:
40
+ return f"Error analyzing file: {str(e)}"
41
+
42
+ # Create Gradio interface
43
+ with gr.Blocks(title="Ada Conversion Assistant") as demo:
44
+ gr.Markdown("# Ada Conversion Assistant")
45
+ gr.Markdown("Upload your Ada files (.ads, .adb, .ada) to analyze business logic and get conversion recommendations.")
46
+
47
+ with gr.Row():
48
+ with gr.Column():
49
+ file_input = gr.File(
50
+ label="Upload Ada File",
51
+ file_types=[".ads", ".adb", ".ada"],
52
+ file_count="single"
53
+ )
54
+ analyze_btn = gr.Button("Analyze", variant="primary")
55
+
56
+ with gr.Column():
57
+ output = gr.Textbox(
58
+ label="Analysis Result",
59
+ lines=20,
60
+ max_lines=30,
61
+ show_copy_button=True
62
+ )
63
+
64
+ analyze_btn.click(
65
+ fn=analyze_ada_file,
66
+ inputs=[file_input],
67
+ outputs=[output]
68
+ )
69
+
70
+ if __name__ == "__main__":
71
+ demo.launch()
pyproject.toml ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "ada-assistant2"
3
+ version = "0.1.0"
4
+ description = "Add your description here"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ dependencies = [
8
+ "gradio>=5.33.2",
9
+ "litellm>=1.72.4",
10
+ "pytest>=8.4.0",
11
+ "python-dotenv>=1.1.0",
12
+ "smolagents>=1.18.0",
13
+ ]
requirements.txt ADDED
@@ -0,0 +1,260 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file was autogenerated by uv via the following command:
2
+ # uv pip compile pyproject.toml -o requirements.txt
3
+ aiofiles==24.1.0
4
+ # via gradio
5
+ aiohappyeyeballs==2.6.1
6
+ # via aiohttp
7
+ aiohttp==3.12.12
8
+ # via litellm
9
+ aiosignal==1.3.2
10
+ # via aiohttp
11
+ annotated-types==0.7.0
12
+ # via pydantic
13
+ anyio==4.9.0
14
+ # via
15
+ # gradio
16
+ # httpx
17
+ # openai
18
+ # starlette
19
+ async-timeout==5.0.1
20
+ # via aiohttp
21
+ attrs==25.3.0
22
+ # via
23
+ # aiohttp
24
+ # jsonschema
25
+ # referencing
26
+ certifi==2025.4.26
27
+ # via
28
+ # httpcore
29
+ # httpx
30
+ # requests
31
+ charset-normalizer==3.4.2
32
+ # via requests
33
+ click==8.2.1
34
+ # via
35
+ # litellm
36
+ # typer
37
+ # uvicorn
38
+ distro==1.9.0
39
+ # via openai
40
+ exceptiongroup==1.3.0
41
+ # via
42
+ # anyio
43
+ # pytest
44
+ fastapi==0.115.12
45
+ # via gradio
46
+ ffmpy==0.6.0
47
+ # via gradio
48
+ filelock==3.18.0
49
+ # via huggingface-hub
50
+ frozenlist==1.7.0
51
+ # via
52
+ # aiohttp
53
+ # aiosignal
54
+ fsspec==2025.5.1
55
+ # via
56
+ # gradio-client
57
+ # huggingface-hub
58
+ gradio==5.33.2
59
+ # via ada-assistant2 (pyproject.toml)
60
+ gradio-client==1.10.3
61
+ # via gradio
62
+ groovy==0.1.2
63
+ # via gradio
64
+ h11==0.16.0
65
+ # via
66
+ # httpcore
67
+ # uvicorn
68
+ hf-xet==1.1.3
69
+ # via huggingface-hub
70
+ httpcore==1.0.9
71
+ # via httpx
72
+ httpx==0.28.1
73
+ # via
74
+ # gradio
75
+ # gradio-client
76
+ # litellm
77
+ # openai
78
+ # safehttpx
79
+ huggingface-hub==0.33.0
80
+ # via
81
+ # gradio
82
+ # gradio-client
83
+ # smolagents
84
+ # tokenizers
85
+ idna==3.10
86
+ # via
87
+ # anyio
88
+ # httpx
89
+ # requests
90
+ # yarl
91
+ importlib-metadata==8.7.0
92
+ # via litellm
93
+ iniconfig==2.1.0
94
+ # via pytest
95
+ jinja2==3.1.6
96
+ # via
97
+ # gradio
98
+ # litellm
99
+ # smolagents
100
+ jiter==0.10.0
101
+ # via openai
102
+ jsonschema==4.24.0
103
+ # via litellm
104
+ jsonschema-specifications==2025.4.1
105
+ # via jsonschema
106
+ litellm==1.72.4
107
+ # via ada-assistant2 (pyproject.toml)
108
+ markdown-it-py==3.0.0
109
+ # via rich
110
+ markupsafe==3.0.2
111
+ # via
112
+ # gradio
113
+ # jinja2
114
+ mdurl==0.1.2
115
+ # via markdown-it-py
116
+ multidict==6.4.4
117
+ # via
118
+ # aiohttp
119
+ # yarl
120
+ numpy==2.2.6
121
+ # via
122
+ # gradio
123
+ # pandas
124
+ openai==1.86.0
125
+ # via litellm
126
+ orjson==3.10.18
127
+ # via gradio
128
+ packaging==25.0
129
+ # via
130
+ # gradio
131
+ # gradio-client
132
+ # huggingface-hub
133
+ # pytest
134
+ pandas==2.3.0
135
+ # via gradio
136
+ pillow==11.2.1
137
+ # via
138
+ # gradio
139
+ # smolagents
140
+ pluggy==1.6.0
141
+ # via pytest
142
+ propcache==0.3.2
143
+ # via
144
+ # aiohttp
145
+ # yarl
146
+ pydantic==2.11.6
147
+ # via
148
+ # fastapi
149
+ # gradio
150
+ # litellm
151
+ # openai
152
+ pydantic-core==2.33.2
153
+ # via pydantic
154
+ pydub==0.25.1
155
+ # via gradio
156
+ pygments==2.19.1
157
+ # via
158
+ # pytest
159
+ # rich
160
+ pytest==8.4.0
161
+ # via ada-assistant2 (pyproject.toml)
162
+ python-dateutil==2.9.0.post0
163
+ # via pandas
164
+ python-dotenv==1.1.0
165
+ # via
166
+ # ada-assistant2 (pyproject.toml)
167
+ # litellm
168
+ # smolagents
169
+ python-multipart==0.0.20
170
+ # via gradio
171
+ pytz==2025.2
172
+ # via pandas
173
+ pyyaml==6.0.2
174
+ # via
175
+ # gradio
176
+ # huggingface-hub
177
+ referencing==0.36.2
178
+ # via
179
+ # jsonschema
180
+ # jsonschema-specifications
181
+ regex==2024.11.6
182
+ # via tiktoken
183
+ requests==2.32.4
184
+ # via
185
+ # huggingface-hub
186
+ # smolagents
187
+ # tiktoken
188
+ rich==14.0.0
189
+ # via
190
+ # smolagents
191
+ # typer
192
+ rpds-py==0.25.1
193
+ # via
194
+ # jsonschema
195
+ # referencing
196
+ ruff==0.11.13
197
+ # via gradio
198
+ safehttpx==0.1.6
199
+ # via gradio
200
+ semantic-version==2.10.0
201
+ # via gradio
202
+ shellingham==1.5.4
203
+ # via typer
204
+ six==1.17.0
205
+ # via python-dateutil
206
+ smolagents==1.18.0
207
+ # via ada-assistant2 (pyproject.toml)
208
+ sniffio==1.3.1
209
+ # via
210
+ # anyio
211
+ # openai
212
+ starlette==0.46.2
213
+ # via
214
+ # fastapi
215
+ # gradio
216
+ tiktoken==0.9.0
217
+ # via litellm
218
+ tokenizers==0.21.1
219
+ # via litellm
220
+ tomli==2.2.1
221
+ # via pytest
222
+ tomlkit==0.13.3
223
+ # via gradio
224
+ tqdm==4.67.1
225
+ # via
226
+ # huggingface-hub
227
+ # openai
228
+ typer==0.16.0
229
+ # via gradio
230
+ typing-extensions==4.14.0
231
+ # via
232
+ # anyio
233
+ # exceptiongroup
234
+ # fastapi
235
+ # gradio
236
+ # gradio-client
237
+ # huggingface-hub
238
+ # multidict
239
+ # openai
240
+ # pydantic
241
+ # pydantic-core
242
+ # referencing
243
+ # rich
244
+ # typer
245
+ # typing-inspection
246
+ # uvicorn
247
+ typing-inspection==0.4.1
248
+ # via pydantic
249
+ tzdata==2025.2
250
+ # via pandas
251
+ urllib3==2.4.0
252
+ # via requests
253
+ uvicorn==0.34.3
254
+ # via gradio
255
+ websockets==15.0.1
256
+ # via gradio-client
257
+ yarl==1.20.1
258
+ # via aiohttp
259
+ zipp==3.23.0
260
+ # via importlib-metadata
src/agents/code_analyzer.py ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Code Analyzer Agent for Ada Conversion Assistant
3
+
4
+ This agent is responsible for:
5
+ 1. Parsing Ada code files
6
+ 2. Extracting project structure and dependencies
7
+ 3. Identifying business logic patterns
8
+ 4. Generating analysis reports for conversion
9
+ """
10
+
11
+ from typing import Dict, Any, Union
12
+ from pathlib import Path
13
+ from smolagents import ToolCallingAgent, FinalAnswerTool
14
+
15
+
16
+ class CodeAnalyzerAgent:
17
+ """Agent specialized in analyzing Ada codebases"""
18
+
19
+ def __init__(self, model: Any):
20
+ # Model passed, create ToolCallingAgent internally with Ada-specific configuration
21
+ # I would have used CodeAgent but kept throwing error no matter what model I used
22
+ self.model = model
23
+ self.code_agent = ToolCallingAgent(
24
+ model=self.model,
25
+ tools=[FinalAnswerTool()]
26
+ )
27
+
28
+ self.supported_extensions = ['.ads', '.adb', '.ada']
29
+ self.analysis_results = {}
30
+
31
+ def _parse_llm_response(self, response: str) -> Dict[str, Any]:
32
+ """Parse LLM response and extract JSON analysis"""
33
+ import json
34
+ import re
35
+
36
+ # Try to extract JSON from the response
37
+ json_match = re.search(r'\{.*\}', response, re.DOTALL)
38
+ if json_match:
39
+ try:
40
+ return json.loads(json_match.group())
41
+ except json.JSONDecodeError:
42
+ pass
43
+
44
+ # Fallback if JSON parsing fails
45
+ return {
46
+ 'file_type': 'unknown',
47
+ 'packages': [],
48
+ 'procedures': [],
49
+ 'functions': [],
50
+ 'types': [],
51
+ 'dependencies': [],
52
+ 'error': "LLM response could not be parsed"
53
+ }
54
+
55
+ def _extract_response_text(self, result) -> str:
56
+ """Extract text response from CodeAgent result"""
57
+ if hasattr(result, 'messages') and result.messages:
58
+ return str(result.messages[-1].content)
59
+ else:
60
+ return str(result)
61
+
62
+ def extract_business_logic(self, ada_file_path: Union[Path, str]) -> Dict[str, Any]:
63
+ """
64
+ Extract business logic patterns from Ada file using LLM
65
+
66
+ Args:
67
+ ada_file_path: Path to Ada file to analyze
68
+
69
+ Returns:
70
+ Business logic analysis with conversion recommendations
71
+ """
72
+ # Convert to Path object if string
73
+ file_path = Path(ada_file_path)
74
+
75
+ # Read Ada file contents
76
+ try:
77
+ with open(file_path, 'r', encoding='utf-8') as f:
78
+ file_content = f.read()
79
+ except Exception as e:
80
+ print(f"Error reading Ada file {file_path}: {e}")
81
+ return self._create_fallback_business_logic_with_error(f"File read error: {str(e)}")
82
+
83
+ # Create business logic analysis prompt
84
+ analysis_prompt = self._create_business_logic_prompt(file_path, file_content)
85
+
86
+ try:
87
+ # Use the code agent to analyze business logic
88
+ result = self.code_agent.run(analysis_prompt)
89
+
90
+ # Extract response text from RunResult
91
+ response_text = self._extract_response_text(result)
92
+
93
+ # Parse the LLM response and ensure structure
94
+ business_logic = self._parse_llm_response(response_text)
95
+ business_logic = self._ensure_business_logic_structure(business_logic)
96
+
97
+ return business_logic
98
+
99
+ except Exception as e:
100
+ print(f"LLM business logic analysis failed: {e}")
101
+ return self._create_fallback_business_logic_with_error(f"LLM analysis failed: {str(e)}")
102
+
103
+ def _create_business_logic_prompt(self, file_path: Path, file_content: str) -> str:
104
+ """Create structured business logic analysis prompt for LLM"""
105
+ return f"""
106
+ Analyze the business logic of this Ada code and return ONLY a JSON object.
107
+ Do not write code or execute anything - just analyze and respond with JSON.
108
+
109
+ Ada File: {file_path}
110
+ Ada Code Content:
111
+ {file_content}
112
+
113
+ Return this exact JSON structure with your analysis:
114
+ {{
115
+ "core_algorithms": ["list of main algorithms and logic"],
116
+ "data_structures": ["list of key data structures and types"],
117
+ "business_rules": ["list of business rules and constraints"],
118
+ "domain_concepts": ["list of domain-specific concepts"],
119
+ "conversion_complexity": "low|medium|high",
120
+ "recommended_approach": "description of conversion strategy"
121
+ }}
122
+
123
+ Focus on identifying:
124
+ - Core business algorithms and computational logic from the Ada code
125
+ - Important data structures, types, and their relationships
126
+ - Business rules, constraints, and validation logic (especially SPARK contracts)
127
+ - Domain-specific concepts and terminology
128
+ - Complexity level for conversion to modern languages like Python/TypeScript
129
+ - Recommended approach for modernization based on the code patterns
130
+
131
+ Return ONLY the JSON object, nothing else.
132
+ """
133
+
134
+ def _ensure_business_logic_structure(self, business_logic: Dict[str, Any]) -> Dict[str, Any]:
135
+ """Ensure business logic response has required structure with defaults"""
136
+ if not isinstance(business_logic, dict):
137
+ business_logic = {}
138
+
139
+ defaults = {
140
+ 'core_algorithms': [],
141
+ 'data_structures': [],
142
+ 'business_rules': [],
143
+ 'domain_concepts': [],
144
+ 'conversion_complexity': 'medium',
145
+ 'recommended_approach': 'Standard modernization approach recommended'
146
+ }
147
+
148
+ for key, default_value in defaults.items():
149
+ if key not in business_logic:
150
+ business_logic[key] = default_value
151
+
152
+ return business_logic
153
+
154
+ def _create_fallback_business_logic_with_error(self, error_msg: str) -> Dict[str, Any]:
155
+ """Create fallback business logic response when analysis fails"""
156
+ return {
157
+ 'core_algorithms': [],
158
+ 'data_structures': [],
159
+ 'business_rules': [],
160
+ 'domain_concepts': [],
161
+ 'conversion_complexity': 'unknown',
162
+ 'recommended_approach': 'Manual analysis recommended due to error',
163
+ 'error': error_msg
164
+ }
src/main.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from pathlib import Path
3
+ from dotenv import load_dotenv
4
+ from smolagents import LiteLLMModel, OpenAIServerModel
5
+ from agents.code_analyzer import CodeAnalyzerAgent
6
+ import json
7
+
8
+ # Load environment variables
9
+ load_dotenv()
10
+
11
+
12
+ def main():
13
+ print("Hello from ada-assistant!")
14
+
15
+ environment = os.getenv("ENVIRONMENT", "development")
16
+ print(f"Running in {environment} mode")
17
+
18
+ # Get model configuration from environment
19
+ if environment == "production":
20
+ model_id = os.getenv("PRODUCTION_MODEL_ID", "gpt-4o-mini")
21
+ model = OpenAIServerModel(
22
+ model_id=model_id,
23
+ api_key=os.environ["OPENAI_API_KEY"]
24
+ )
25
+ else:
26
+ model_id = os.getenv("DEVELOPMENT_MODEL_ID", "ollama/llama3.2")
27
+ model = LiteLLMModel(model_id=model_id)
28
+
29
+ print(f"Using model: {model_id}")
30
+
31
+ # Initialize CodeAnalyzerAgent with model (it will create CodeAgent internally)
32
+ analyzer = CodeAnalyzerAgent(model)
33
+
34
+ print(f"CodeAnalyzerAgent initialized successfully with {len(analyzer.supported_extensions)} supported file types!")
35
+
36
+ # Test extract_business_logic with tictactoe.ads file
37
+ print("\n" + "="*50)
38
+ print("Testing extract_business_logic with tictactoe.ads")
39
+ print("="*50)
40
+
41
+ tictactoe_file = Path(__file__).parent.parent / "tests" / "tictactoe.ads"
42
+
43
+ if tictactoe_file.exists():
44
+ print(f"Analyzing Ada file: {tictactoe_file}")
45
+
46
+ print("\nExtracting business logic...")
47
+ result = analyzer.extract_business_logic(tictactoe_file)
48
+
49
+ print("\nBusiness Logic Analysis:")
50
+ print(json.dumps(result, indent=2, default=str))
51
+ else:
52
+ print(f"Test file not found: {tictactoe_file}")
53
+
54
+
55
+ if __name__ == "__main__":
56
+ main()
tests/test_code_analyzer_agent.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Unit tests for CodeAnalyzerAgent
3
+ """
4
+
5
+ import pytest
6
+ from pathlib import Path
7
+ from unittest.mock import Mock, MagicMock
8
+ from src.agents.code_analyzer import CodeAnalyzerAgent
9
+
10
+
11
+ class TestCodeAnalyzerAgent:
12
+ """Test suite for CodeAnalyzerAgent"""
13
+
14
+ @pytest.fixture
15
+ def mock_model(self):
16
+ """Mock model for testing"""
17
+ return Mock()
18
+
19
+ @pytest.fixture
20
+ def analyzer_agent(self, mock_model):
21
+ """Create CodeAnalyzerAgent instance with mocked model"""
22
+ return CodeAnalyzerAgent(mock_model)
23
+
24
+ def test_initialization_with_model(self, mock_model):
25
+ """Test agent initialization with model parameter"""
26
+ agent = CodeAnalyzerAgent(mock_model)
27
+
28
+ assert agent.model == mock_model
29
+ assert agent.supported_extensions == ['.ads', '.adb', '.ada']
30
+ assert agent.analysis_results == {}
31
+ assert hasattr(agent, 'code_agent')
32
+
33
+
34
+ def test_extract_business_logic_with_ada_file(self, mock_model, tmp_path):
35
+ """Test extract_business_logic with Ada file path and mocked LLM response"""
36
+ from unittest.mock import MagicMock
37
+
38
+ # Mock the CodeAgent run method to return business logic analysis
39
+ mock_result = MagicMock()
40
+ mock_result.messages = []
41
+ mock_message = MagicMock()
42
+ mock_message.content = '''
43
+ {
44
+ "core_algorithms": ["TicTacToe game logic", "Board state evaluation", "Win condition checking"],
45
+ "data_structures": ["Board array structure", "Position type system", "Slot enumeration"],
46
+ "business_rules": ["SPARK verification contracts", "Pre/post conditions", "Game state constraints"],
47
+ "domain_concepts": ["Game board management", "Player moves", "Win detection"],
48
+ "conversion_complexity": "medium",
49
+ "recommended_approach": "Translate to Python classes with similar structure"
50
+ }
51
+ '''
52
+ mock_result.messages.append(mock_message)
53
+
54
+ analyzer_agent = CodeAnalyzerAgent(mock_model)
55
+ analyzer_agent.code_agent.run = MagicMock(return_value=mock_result)
56
+
57
+ # Create test Ada file
58
+ ada_file = tmp_path / "tictactoe.ads"
59
+ ada_file.write_text("""
60
+ package Tictactoe
61
+ with SPARK_Mode => On
62
+ is
63
+ type Slot is (Empty, Player, Computer);
64
+ type Pos is new Integer range 1 .. 3;
65
+ type Column is array (Pos) of Slot;
66
+ type Board is array (Pos) of Column;
67
+
68
+ My_Board : Board := (others => (others => Empty));
69
+
70
+ procedure Initialize
71
+ with Post => Num_Free_Slots = 9;
72
+
73
+ procedure Player_Play (S : String)
74
+ with Pre => not Is_Full and Won = Empty,
75
+ Post => Num_Free_Slots = Num_Free_Slots'Old - 1;
76
+
77
+ function Won return Slot;
78
+ function Is_Full return Boolean is (Num_Free_Slots = 0);
79
+ end Tictactoe;
80
+ """.strip())
81
+
82
+ result = analyzer_agent.extract_business_logic(ada_file)
83
+
84
+ # Verify result structure
85
+ assert isinstance(result, dict)
86
+ assert 'core_algorithms' in result
87
+ assert 'data_structures' in result
88
+ assert 'business_rules' in result
89
+ assert 'domain_concepts' in result
90
+ assert 'conversion_complexity' in result
91
+ assert 'recommended_approach' in result
92
+
93
+ # Verify LLM found business logic concepts
94
+ assert "TicTacToe game logic" in result['core_algorithms']
95
+ assert "Board array structure" in result['data_structures']
96
+ assert result['conversion_complexity'] == "medium"
97
+
98
+ # Verify LLM analysis was called
99
+ analyzer_agent.code_agent.run.assert_called_once()
100
+
tests/tictactoe.ads ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package Tictactoe
2
+ with SPARK_Mode => On
3
+ is
4
+
5
+ type Slot is (Empty, Player, Computer);
6
+ type Pos is new Integer range 1 .. 3;
7
+ type Column is array (Pos) of Slot;
8
+ type Board is array (Pos) of Column;
9
+
10
+ My_Board : Board := (others => (others => Empty));
11
+
12
+ -- Game operations
13
+
14
+ procedure Initialize
15
+ with Post => Num_Free_Slots = 9;
16
+
17
+ procedure Player_Play (S : String)
18
+ with Pre => not Is_Full and Won = Empty,
19
+ Post => Num_Free_Slots = Num_Free_Slots'Old - 1;
20
+
21
+ procedure Computer_Play
22
+ with Pre => not Is_Full and Won = Empty,
23
+ Post => Num_Free_Slots = Num_Free_Slots'Old - 1;
24
+
25
+ procedure Display;
26
+
27
+ function Won return Slot;
28
+
29
+ function One_Free_Slot (X, Y : Pos) return Integer is
30
+ (if My_Board(X)(Y) = Empty then 1 else 0);
31
+
32
+ function Count_Free_Slots (X, Y : Pos) return Integer is
33
+ (One_Free_Slot(1,1) +
34
+ (if Y >= 2 then One_Free_Slot(1,2) else 0) +
35
+ (if Y >= 3 then One_Free_Slot(1,3) else 0) +
36
+ (if X >= 2 then
37
+ One_Free_Slot(2,1) +
38
+ (if Y >= 2 then One_Free_Slot(2,2) else 0) +
39
+ (if Y >= 3 then One_Free_Slot(2,3) else 0)
40
+ else 0) +
41
+ (if X >= 3 then
42
+ One_Free_Slot(3,1) +
43
+ (if Y >= 2 then One_Free_Slot(3,2) else 0) +
44
+ (if Y >= 3 then One_Free_Slot(3,3) else 0)
45
+ else 0));
46
+
47
+ function Num_Free_Slots return Natural is
48
+ (Count_Free_Slots(3,3));
49
+
50
+ function Is_Full return Boolean is (Num_Free_Slots = 0);
51
+
52
+ end Tictactoe;
uv.lock ADDED
The diff for this file is too large to render. See raw diff