praisonai-code 0.0.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.
- praisonai_code/__init__.py +17 -0
- praisonai_code/cli/__init__.py +12 -0
- praisonai_code/cli/_forward_shim.py +10 -0
- praisonai_code/cli/_paths.py +88 -0
- praisonai_code/cli/_warnings.py +58 -0
- praisonai_code/cli/app.py +757 -0
- praisonai_code/cli/approval_backend.py +272 -0
- praisonai_code/cli/branding.py +94 -0
- praisonai_code/cli/commands/__init__.py +114 -0
- praisonai_code/cli/commands/acp.py +80 -0
- praisonai_code/cli/commands/agent.py +116 -0
- praisonai_code/cli/commands/agents.py +80 -0
- praisonai_code/cli/commands/app.py +139 -0
- praisonai_code/cli/commands/attach.py +95 -0
- praisonai_code/cli/commands/audit.py +102 -0
- praisonai_code/cli/commands/auth.py +508 -0
- praisonai_code/cli/commands/batch.py +848 -0
- praisonai_code/cli/commands/benchmark.py +286 -0
- praisonai_code/cli/commands/browser.py +299 -0
- praisonai_code/cli/commands/call.py +45 -0
- praisonai_code/cli/commands/chat.py +332 -0
- praisonai_code/cli/commands/checkpoint.py +170 -0
- praisonai_code/cli/commands/code.py +276 -0
- praisonai_code/cli/commands/command.py +114 -0
- praisonai_code/cli/commands/commit.py +47 -0
- praisonai_code/cli/commands/completion.py +333 -0
- praisonai_code/cli/commands/config.py +681 -0
- praisonai_code/cli/commands/context.py +414 -0
- praisonai_code/cli/commands/daemon.py +203 -0
- praisonai_code/cli/commands/debug.py +142 -0
- praisonai_code/cli/commands/deploy.py +71 -0
- praisonai_code/cli/commands/diag.py +55 -0
- praisonai_code/cli/commands/docs.py +1575 -0
- praisonai_code/cli/commands/doctor.py +332 -0
- praisonai_code/cli/commands/endpoints.py +51 -0
- praisonai_code/cli/commands/environment.py +179 -0
- praisonai_code/cli/commands/eval.py +131 -0
- praisonai_code/cli/commands/examples.py +953 -0
- praisonai_code/cli/commands/flow.py +436 -0
- praisonai_code/cli/commands/github.py +752 -0
- praisonai_code/cli/commands/hooks.py +74 -0
- praisonai_code/cli/commands/init.py +174 -0
- praisonai_code/cli/commands/knowledge.py +440 -0
- praisonai_code/cli/commands/langextract.py +120 -0
- praisonai_code/cli/commands/langfuse.py +984 -0
- praisonai_code/cli/commands/loop.py +211 -0
- praisonai_code/cli/commands/lsp.py +112 -0
- praisonai_code/cli/commands/managed.py +659 -0
- praisonai_code/cli/commands/mcp.py +763 -0
- praisonai_code/cli/commands/memory.py +298 -0
- praisonai_code/cli/commands/models.py +264 -0
- praisonai_code/cli/commands/n8n.py +326 -0
- praisonai_code/cli/commands/obs.py +19 -0
- praisonai_code/cli/commands/package.py +76 -0
- praisonai_code/cli/commands/paths.py +106 -0
- praisonai_code/cli/commands/permissions.py +272 -0
- praisonai_code/cli/commands/plugins.py +609 -0
- praisonai_code/cli/commands/port.py +530 -0
- praisonai_code/cli/commands/profile.py +466 -0
- praisonai_code/cli/commands/publish.py +193 -0
- praisonai_code/cli/commands/rag.py +913 -0
- praisonai_code/cli/commands/realtime.py +52 -0
- praisonai_code/cli/commands/recipe.py +684 -0
- praisonai_code/cli/commands/registry.py +59 -0
- praisonai_code/cli/commands/replay.py +830 -0
- praisonai_code/cli/commands/research.py +49 -0
- praisonai_code/cli/commands/retrieval.py +377 -0
- praisonai_code/cli/commands/rules.py +71 -0
- praisonai_code/cli/commands/run.py +1573 -0
- praisonai_code/cli/commands/sandbox.py +371 -0
- praisonai_code/cli/commands/schedule.py +529 -0
- praisonai_code/cli/commands/serve.py +690 -0
- praisonai_code/cli/commands/session.py +450 -0
- praisonai_code/cli/commands/setup.py +174 -0
- praisonai_code/cli/commands/skills.py +545 -0
- praisonai_code/cli/commands/standardise.py +711 -0
- praisonai_code/cli/commands/templates.py +54 -0
- praisonai_code/cli/commands/test.py +558 -0
- praisonai_code/cli/commands/todo.py +74 -0
- praisonai_code/cli/commands/tools.py +205 -0
- praisonai_code/cli/commands/traces.py +145 -0
- praisonai_code/cli/commands/tracker.py +852 -0
- praisonai_code/cli/commands/train.py +613 -0
- praisonai_code/cli/commands/ui.py +172 -0
- praisonai_code/cli/commands/up.py +354 -0
- praisonai_code/cli/commands/validate.py +291 -0
- praisonai_code/cli/commands/version.py +101 -0
- praisonai_code/cli/commands/workflow.py +97 -0
- praisonai_code/cli/config_loader.py +437 -0
- praisonai_code/cli/configuration/__init__.py +27 -0
- praisonai_code/cli/configuration/config.schema.json +57 -0
- praisonai_code/cli/configuration/credentials.py +446 -0
- praisonai_code/cli/configuration/loader.py +364 -0
- praisonai_code/cli/configuration/model_resolver.py +161 -0
- praisonai_code/cli/configuration/oauth.py +389 -0
- praisonai_code/cli/configuration/paths.py +224 -0
- praisonai_code/cli/configuration/resolver.py +687 -0
- praisonai_code/cli/configuration/schema.py +317 -0
- praisonai_code/cli/execution/__init__.py +99 -0
- praisonai_code/cli/execution/core.py +208 -0
- praisonai_code/cli/execution/profiler.py +898 -0
- praisonai_code/cli/execution/request.py +85 -0
- praisonai_code/cli/execution/result.py +74 -0
- praisonai_code/cli/fallback_schema.py +416 -0
- praisonai_code/cli/features/__init__.py +278 -0
- praisonai_code/cli/features/_endpoint_registry.py +64 -0
- praisonai_code/cli/features/_search_registry.py +43 -0
- praisonai_code/cli/features/acp.py +236 -0
- praisonai_code/cli/features/action_orchestrator.py +576 -0
- praisonai_code/cli/features/agent_scheduler.py +773 -0
- praisonai_code/cli/features/agent_tools.py +603 -0
- praisonai_code/cli/features/agents.py +397 -0
- praisonai_code/cli/features/at_mentions.py +471 -0
- praisonai_code/cli/features/audit_cli.py +270 -0
- praisonai_code/cli/features/auto_memory.py +182 -0
- praisonai_code/cli/features/auto_mode.py +552 -0
- praisonai_code/cli/features/autonomy_mode.py +546 -0
- praisonai_code/cli/features/background.py +356 -0
- praisonai_code/cli/features/base.py +168 -0
- praisonai_code/cli/features/benchmark.py +1462 -0
- praisonai_code/cli/features/capabilities.py +1326 -0
- praisonai_code/cli/features/checkpoints.py +345 -0
- praisonai_code/cli/features/cli_profiler.py +335 -0
- praisonai_code/cli/features/code_intelligence.py +666 -0
- praisonai_code/cli/features/compaction.py +294 -0
- praisonai_code/cli/features/compare.py +534 -0
- praisonai_code/cli/features/config_hierarchy.py +366 -0
- praisonai_code/cli/features/context_manager.py +597 -0
- praisonai_code/cli/features/cost_tracker.py +514 -0
- praisonai_code/cli/features/csv_test_runner.py +736 -0
- praisonai_code/cli/features/custom_definitions.py +790 -0
- praisonai_code/cli/features/debug.py +810 -0
- praisonai_code/cli/features/deploy.py +605 -0
- praisonai_code/cli/features/diag.py +289 -0
- praisonai_code/cli/features/display_jsonl.py +173 -0
- praisonai_code/cli/features/doctor/__init__.py +63 -0
- praisonai_code/cli/features/doctor/checks/__init__.py +29 -0
- praisonai_code/cli/features/doctor/checks/acp_checks.py +220 -0
- praisonai_code/cli/features/doctor/checks/bot_checks.py +340 -0
- praisonai_code/cli/features/doctor/checks/config_checks.py +373 -0
- praisonai_code/cli/features/doctor/checks/db_checks.py +366 -0
- praisonai_code/cli/features/doctor/checks/env_checks.py +637 -0
- praisonai_code/cli/features/doctor/checks/gateway_checks.py +387 -0
- praisonai_code/cli/features/doctor/checks/lsp_checks.py +231 -0
- praisonai_code/cli/features/doctor/checks/mcp_checks.py +367 -0
- praisonai_code/cli/features/doctor/checks/memory_checks.py +268 -0
- praisonai_code/cli/features/doctor/checks/network_checks.py +251 -0
- praisonai_code/cli/features/doctor/checks/obs_checks.py +328 -0
- praisonai_code/cli/features/doctor/checks/packaging_checks.py +422 -0
- praisonai_code/cli/features/doctor/checks/performance_checks.py +235 -0
- praisonai_code/cli/features/doctor/checks/permissions_checks.py +259 -0
- praisonai_code/cli/features/doctor/checks/runtime_checks.py +650 -0
- praisonai_code/cli/features/doctor/checks/runtime_migration_checks.py +220 -0
- praisonai_code/cli/features/doctor/checks/selftest_checks.py +322 -0
- praisonai_code/cli/features/doctor/checks/serve_checks.py +426 -0
- praisonai_code/cli/features/doctor/checks/skills_checks.py +327 -0
- praisonai_code/cli/features/doctor/checks/tools_checks.py +371 -0
- praisonai_code/cli/features/doctor/engine.py +266 -0
- praisonai_code/cli/features/doctor/formatters.py +377 -0
- praisonai_code/cli/features/doctor/handler.py +564 -0
- praisonai_code/cli/features/doctor/models.py +276 -0
- praisonai_code/cli/features/doctor/registry.py +239 -0
- praisonai_code/cli/features/endpoints.py +1016 -0
- praisonai_code/cli/features/eval.py +559 -0
- praisonai_code/cli/features/examples.py +707 -0
- praisonai_code/cli/features/external_agents.py +231 -0
- praisonai_code/cli/features/fast_context.py +410 -0
- praisonai_code/cli/features/file_history.py +320 -0
- praisonai_code/cli/features/flow_display.py +566 -0
- praisonai_code/cli/features/git_attribution.py +159 -0
- praisonai_code/cli/features/git_integration.py +651 -0
- praisonai_code/cli/features/guardrail.py +171 -0
- praisonai_code/cli/features/handoff.py +252 -0
- praisonai_code/cli/features/hooks.py +583 -0
- praisonai_code/cli/features/hybrid_workflow.py +391 -0
- praisonai_code/cli/features/image.py +384 -0
- praisonai_code/cli/features/interactive_core_headless.py +450 -0
- praisonai_code/cli/features/interactive_runtime.py +600 -0
- praisonai_code/cli/features/interactive_test_harness.py +537 -0
- praisonai_code/cli/features/interactive_tools.py +428 -0
- praisonai_code/cli/features/interactive_tui.py +603 -0
- praisonai_code/cli/features/job_workflow.py +906 -0
- praisonai_code/cli/features/jobs.py +632 -0
- praisonai_code/cli/features/knowledge.py +531 -0
- praisonai_code/cli/features/knowledge_cli.py +438 -0
- praisonai_code/cli/features/lite.py +244 -0
- praisonai_code/cli/features/logs.py +200 -0
- praisonai_code/cli/features/lsp_cli.py +225 -0
- praisonai_code/cli/features/lsp_diagnostics.py +185 -0
- praisonai_code/cli/features/mcp.py +344 -0
- praisonai_code/cli/features/message_queue.py +587 -0
- praisonai_code/cli/features/metrics.py +210 -0
- praisonai_code/cli/features/migrate.py +1329 -0
- praisonai_code/cli/features/migration_flow.py +463 -0
- praisonai_code/cli/features/migration_spec.py +276 -0
- praisonai_code/cli/features/n8n.py +703 -0
- praisonai_code/cli/features/observability.py +293 -0
- praisonai_code/cli/features/ollama.py +361 -0
- praisonai_code/cli/features/output_modes.py +155 -0
- praisonai_code/cli/features/output_style.py +273 -0
- praisonai_code/cli/features/package.py +631 -0
- praisonai_code/cli/features/performance.py +308 -0
- praisonai_code/cli/features/persistence.py +636 -0
- praisonai_code/cli/features/profiler/__init__.py +81 -0
- praisonai_code/cli/features/profiler/core.py +558 -0
- praisonai_code/cli/features/profiler/optimizations.py +652 -0
- praisonai_code/cli/features/profiler/suite.py +386 -0
- praisonai_code/cli/features/queue/__init__.py +73 -0
- praisonai_code/cli/features/queue/manager.py +435 -0
- praisonai_code/cli/features/queue/models.py +289 -0
- praisonai_code/cli/features/queue/persistence.py +564 -0
- praisonai_code/cli/features/queue/scheduler.py +529 -0
- praisonai_code/cli/features/queue/worker.py +400 -0
- praisonai_code/cli/features/recipe.py +2187 -0
- praisonai_code/cli/features/recipe_creator.py +996 -0
- praisonai_code/cli/features/recipe_optimizer.py +1364 -0
- praisonai_code/cli/features/recipe_prompts.py +226 -0
- praisonai_code/cli/features/registry.py +229 -0
- praisonai_code/cli/features/repo_map.py +860 -0
- praisonai_code/cli/features/router.py +466 -0
- praisonai_code/cli/features/safe_shell.py +427 -0
- praisonai_code/cli/features/sandbox_cli.py +283 -0
- praisonai_code/cli/features/sandbox_executor.py +536 -0
- praisonai_code/cli/features/sdk_knowledge.py +500 -0
- praisonai_code/cli/features/session.py +222 -0
- praisonai_code/cli/features/session_checkpoints.py +208 -0
- praisonai_code/cli/features/setup/__init__.py +9 -0
- praisonai_code/cli/features/setup/handler.py +355 -0
- praisonai_code/cli/features/setup/templates.py +62 -0
- praisonai_code/cli/features/skills.py +940 -0
- praisonai_code/cli/features/slash_commands.py +692 -0
- praisonai_code/cli/features/telemetry.py +179 -0
- praisonai_code/cli/features/templates.py +1390 -0
- praisonai_code/cli/features/thinking.py +343 -0
- praisonai_code/cli/features/todo.py +334 -0
- praisonai_code/cli/features/tools.py +680 -0
- praisonai_code/cli/features/tui/__init__.py +83 -0
- praisonai_code/cli/features/tui/app.py +871 -0
- praisonai_code/cli/features/tui/cli.py +580 -0
- praisonai_code/cli/features/tui/config.py +150 -0
- praisonai_code/cli/features/tui/debug.py +526 -0
- praisonai_code/cli/features/tui/events.py +99 -0
- praisonai_code/cli/features/tui/mock_provider.py +328 -0
- praisonai_code/cli/features/tui/orchestrator.py +652 -0
- praisonai_code/cli/features/tui/screens/__init__.py +50 -0
- praisonai_code/cli/features/tui/screens/help.py +157 -0
- praisonai_code/cli/features/tui/screens/main.py +568 -0
- praisonai_code/cli/features/tui/screens/queue.py +174 -0
- praisonai_code/cli/features/tui/screens/session.py +124 -0
- praisonai_code/cli/features/tui/screens/settings.py +148 -0
- praisonai_code/cli/features/tui/session_store.py +198 -0
- praisonai_code/cli/features/tui/widgets/__init__.py +56 -0
- praisonai_code/cli/features/tui/widgets/chat.py +263 -0
- praisonai_code/cli/features/tui/widgets/command_popup.py +258 -0
- praisonai_code/cli/features/tui/widgets/composer.py +292 -0
- praisonai_code/cli/features/tui/widgets/file_popup.py +207 -0
- praisonai_code/cli/features/tui/widgets/queue_panel.py +223 -0
- praisonai_code/cli/features/tui/widgets/status.py +181 -0
- praisonai_code/cli/features/tui/widgets/tool_panel.py +307 -0
- praisonai_code/cli/features/wizard.py +289 -0
- praisonai_code/cli/features/workflow.py +802 -0
- praisonai_code/cli/features/yaml_utils.py +321 -0
- praisonai_code/cli/interactive/__init__.py +48 -0
- praisonai_code/cli/interactive/async_tui.py +1218 -0
- praisonai_code/cli/interactive/config.py +139 -0
- praisonai_code/cli/interactive/core.py +618 -0
- praisonai_code/cli/interactive/events.py +131 -0
- praisonai_code/cli/interactive/frontends/__init__.py +31 -0
- praisonai_code/cli/interactive/frontends/rich_frontend.py +462 -0
- praisonai_code/cli/interactive/frontends/textual_frontend.py +157 -0
- praisonai_code/cli/interactive/praison_io.py +502 -0
- praisonai_code/cli/interactive/repl.py +297 -0
- praisonai_code/cli/interactive/split_tui.py +456 -0
- praisonai_code/cli/interactive/tui_app.py +457 -0
- praisonai_code/cli/langfuse_client.py +360 -0
- praisonai_code/cli/main.py +7421 -0
- praisonai_code/cli/output/__init__.py +25 -0
- praisonai_code/cli/output/console.py +456 -0
- praisonai_code/cli/output/event_bridge.py +191 -0
- praisonai_code/cli/schedule_cli.py +54 -0
- praisonai_code/cli/schema_provider.py +23 -0
- praisonai_code/cli/session/__init__.py +16 -0
- praisonai_code/cli/session/resume.py +148 -0
- praisonai_code/cli/session/unified.py +548 -0
- praisonai_code/cli/state/__init__.py +31 -0
- praisonai_code/cli/state/identifiers.py +161 -0
- praisonai_code/cli/state/project_sessions.py +383 -0
- praisonai_code/cli/state/sessions.py +390 -0
- praisonai_code/cli/ui/__init__.py +160 -0
- praisonai_code/cli/ui/config.py +46 -0
- praisonai_code/cli/ui/events.py +61 -0
- praisonai_code/cli/ui/mg_backend.py +342 -0
- praisonai_code/cli/ui/plain.py +133 -0
- praisonai_code/cli/ui/rich_backend.py +162 -0
- praisonai_code/cli/unified_schema.py +655 -0
- praisonai_code/cli/utils/env_utils.py +126 -0
- praisonai_code/cli/utils/project.py +131 -0
- praisonai_code/cli_backends/__init__.py +73 -0
- praisonai_code/cli_backends/claude.py +373 -0
- praisonai_code/cli_backends/registry.py +113 -0
- praisonai_code/runtime/__init__.py +36 -0
- praisonai_code/runtime/__main__.py +81 -0
- praisonai_code/runtime/client.py +131 -0
- praisonai_code/runtime/descriptor.py +209 -0
- praisonai_code/runtime/server.py +356 -0
- praisonai_code-0.0.1.dist-info/METADATA +80 -0
- praisonai_code-0.0.1.dist-info/RECORD +309 -0
- praisonai_code-0.0.1.dist-info/WHEEL +5 -0
- praisonai_code-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent-Driven Migration Flow.
|
|
3
|
+
|
|
4
|
+
Uses AgentFlow with specialized agents to migrate code from various
|
|
5
|
+
agent frameworks to PraisonAI format.
|
|
6
|
+
|
|
7
|
+
Architecture:
|
|
8
|
+
1. Analyzer Agent - Scans all files, understands relationships
|
|
9
|
+
2. Converter Agent - Transforms code using migration spec
|
|
10
|
+
3. Evaluator Agent - Uses LLM-as-Judge to validate conversion
|
|
11
|
+
4. Error Agent - Analyzes failures and suggests fixes
|
|
12
|
+
|
|
13
|
+
The flow includes a retry loop (max 3 iterations) for error recovery.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import os
|
|
17
|
+
import logging
|
|
18
|
+
from dataclasses import dataclass, field
|
|
19
|
+
from typing import Dict, List, Optional
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class FileContext:
|
|
27
|
+
"""Context for a single file in the migration."""
|
|
28
|
+
path: str
|
|
29
|
+
content: str
|
|
30
|
+
patterns_found: List[str] = field(default_factory=list)
|
|
31
|
+
converted_content: Optional[str] = None
|
|
32
|
+
errors: List[str] = field(default_factory=list)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class MigrationContext:
|
|
37
|
+
"""Complete context for multi-file migration."""
|
|
38
|
+
source_path: str
|
|
39
|
+
files: Dict[str, FileContext] = field(default_factory=dict)
|
|
40
|
+
all_imports: List[str] = field(default_factory=list)
|
|
41
|
+
all_classes: List[str] = field(default_factory=list)
|
|
42
|
+
all_functions: List[str] = field(default_factory=list)
|
|
43
|
+
cross_file_deps: Dict[str, List[str]] = field(default_factory=dict)
|
|
44
|
+
|
|
45
|
+
def to_prompt(self) -> str:
|
|
46
|
+
"""Convert context to a prompt for LLM agents."""
|
|
47
|
+
lines = [
|
|
48
|
+
f"# Project Analysis: {self.source_path}",
|
|
49
|
+
f"Total files: {len(self.files)}",
|
|
50
|
+
"",
|
|
51
|
+
"## Files:",
|
|
52
|
+
]
|
|
53
|
+
for path, ctx in self.files.items():
|
|
54
|
+
lines.append(f"- {path}: {len(ctx.content)} chars, patterns: {ctx.patterns_found}")
|
|
55
|
+
|
|
56
|
+
if self.cross_file_deps:
|
|
57
|
+
lines.append("")
|
|
58
|
+
lines.append("## Cross-file Dependencies:")
|
|
59
|
+
for file, deps in self.cross_file_deps.items():
|
|
60
|
+
lines.append(f"- {file} imports from: {deps}")
|
|
61
|
+
|
|
62
|
+
return "\n".join(lines)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass
|
|
66
|
+
class MigrationResult:
|
|
67
|
+
"""Result of the migration flow."""
|
|
68
|
+
success: bool
|
|
69
|
+
files_converted: int = 0
|
|
70
|
+
files_failed: int = 0
|
|
71
|
+
iterations: int = 0
|
|
72
|
+
evaluation_score: float = 0.0
|
|
73
|
+
converted_files: Dict[str, str] = field(default_factory=dict)
|
|
74
|
+
errors: List[str] = field(default_factory=list)
|
|
75
|
+
warnings: List[str] = field(default_factory=list)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class MigrationFlow:
|
|
79
|
+
"""
|
|
80
|
+
Agent-driven migration flow using AgentFlow.
|
|
81
|
+
|
|
82
|
+
Uses specialized agents to analyze, convert, evaluate, and fix
|
|
83
|
+
code migration from various agent frameworks to PraisonAI.
|
|
84
|
+
|
|
85
|
+
Example:
|
|
86
|
+
flow = MigrationFlow(
|
|
87
|
+
source_path="./my-project",
|
|
88
|
+
output_path="./converted",
|
|
89
|
+
max_retries=3
|
|
90
|
+
)
|
|
91
|
+
result = flow.run()
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
def __init__(
|
|
95
|
+
self,
|
|
96
|
+
source_path: str,
|
|
97
|
+
output_path: Optional[str] = None,
|
|
98
|
+
max_retries: int = 3,
|
|
99
|
+
llm: str = "gpt-4o-mini",
|
|
100
|
+
dry_run: bool = True,
|
|
101
|
+
):
|
|
102
|
+
self.source_path = source_path
|
|
103
|
+
self.output_path = output_path or f"{source_path}_converted"
|
|
104
|
+
self.max_retries = max_retries
|
|
105
|
+
self.llm = llm
|
|
106
|
+
self.dry_run = dry_run
|
|
107
|
+
self._context: Optional[MigrationContext] = None
|
|
108
|
+
self._spec = None
|
|
109
|
+
|
|
110
|
+
def _gather_files(self) -> MigrationContext:
|
|
111
|
+
"""Gather all Python files and build context."""
|
|
112
|
+
context = MigrationContext(source_path=self.source_path)
|
|
113
|
+
|
|
114
|
+
source = Path(self.source_path)
|
|
115
|
+
|
|
116
|
+
if source.is_file():
|
|
117
|
+
# Single file
|
|
118
|
+
with open(source, 'r', encoding='utf-8') as f:
|
|
119
|
+
content = f.read()
|
|
120
|
+
context.files[str(source)] = FileContext(
|
|
121
|
+
path=str(source),
|
|
122
|
+
content=content
|
|
123
|
+
)
|
|
124
|
+
else:
|
|
125
|
+
# Directory - scan for Python files
|
|
126
|
+
exclude_dirs = {'venv', '.venv', '__pycache__', '.git', 'node_modules', '.tox', 'dist', 'build'}
|
|
127
|
+
|
|
128
|
+
for root, dirs, files in os.walk(source):
|
|
129
|
+
# Filter out excluded directories
|
|
130
|
+
dirs[:] = [d for d in dirs if d not in exclude_dirs]
|
|
131
|
+
|
|
132
|
+
for filename in files:
|
|
133
|
+
if filename.endswith('.py'):
|
|
134
|
+
filepath = os.path.join(root, filename)
|
|
135
|
+
try:
|
|
136
|
+
with open(filepath, 'r', encoding='utf-8') as f:
|
|
137
|
+
content = f.read()
|
|
138
|
+
rel_path = os.path.relpath(filepath, source)
|
|
139
|
+
context.files[rel_path] = FileContext(
|
|
140
|
+
path=rel_path,
|
|
141
|
+
content=content
|
|
142
|
+
)
|
|
143
|
+
except Exception as e:
|
|
144
|
+
logger.warning(f"Could not read {filepath}: {e}")
|
|
145
|
+
|
|
146
|
+
# Analyze cross-file dependencies
|
|
147
|
+
self._analyze_dependencies(context)
|
|
148
|
+
|
|
149
|
+
return context
|
|
150
|
+
|
|
151
|
+
def _analyze_dependencies(self, context: MigrationContext) -> None:
|
|
152
|
+
"""Analyze imports and cross-file dependencies."""
|
|
153
|
+
import re
|
|
154
|
+
|
|
155
|
+
for path, file_ctx in context.files.items():
|
|
156
|
+
# Extract imports
|
|
157
|
+
import_pattern = r'^(?:from\s+(\S+)\s+)?import\s+(.+)$'
|
|
158
|
+
for line in file_ctx.content.split('\n'):
|
|
159
|
+
match = re.match(import_pattern, line.strip())
|
|
160
|
+
if match:
|
|
161
|
+
module = match.group(1) or match.group(2).split(',')[0].strip()
|
|
162
|
+
context.all_imports.append(module)
|
|
163
|
+
|
|
164
|
+
# Check if it's a local import
|
|
165
|
+
for other_path in context.files:
|
|
166
|
+
other_module = other_path.replace('/', '.').replace('.py', '')
|
|
167
|
+
if module.startswith(other_module) or other_module.endswith(module):
|
|
168
|
+
if path not in context.cross_file_deps:
|
|
169
|
+
context.cross_file_deps[path] = []
|
|
170
|
+
context.cross_file_deps[path].append(other_path)
|
|
171
|
+
|
|
172
|
+
# Extract class and function names
|
|
173
|
+
class_pattern = r'^class\s+(\w+)'
|
|
174
|
+
func_pattern = r'^def\s+(\w+)'
|
|
175
|
+
for line in file_ctx.content.split('\n'):
|
|
176
|
+
match = re.match(class_pattern, line)
|
|
177
|
+
if match:
|
|
178
|
+
context.all_classes.append(match.group(1))
|
|
179
|
+
match = re.match(func_pattern, line)
|
|
180
|
+
if match:
|
|
181
|
+
context.all_functions.append(match.group(1))
|
|
182
|
+
|
|
183
|
+
def _create_agents(self):
|
|
184
|
+
"""Create the specialized migration agents."""
|
|
185
|
+
# Lazy import to avoid circular dependencies
|
|
186
|
+
from praisonaiagents import Agent
|
|
187
|
+
from .migration_spec import get_default_spec, PRAISONAI_REFERENCE
|
|
188
|
+
|
|
189
|
+
self._spec = get_default_spec()
|
|
190
|
+
spec_prompt = self._spec.to_prompt()
|
|
191
|
+
|
|
192
|
+
# Analyzer Agent
|
|
193
|
+
analyzer = Agent(
|
|
194
|
+
name="Analyzer",
|
|
195
|
+
role="Code Analyzer",
|
|
196
|
+
goal="Analyze source code to identify agent patterns and dependencies",
|
|
197
|
+
instructions=f"""You are a code analysis expert. Analyze the provided source code to:
|
|
198
|
+
1. Identify all agent-related patterns (Agent, Task, Team, Workflow definitions)
|
|
199
|
+
2. Map dependencies between files
|
|
200
|
+
3. Identify import patterns that need conversion
|
|
201
|
+
4. List all classes and functions that are part of the agent system
|
|
202
|
+
|
|
203
|
+
Use this specification to identify patterns:
|
|
204
|
+
{spec_prompt}
|
|
205
|
+
|
|
206
|
+
Output a structured analysis with:
|
|
207
|
+
- List of detected patterns per file
|
|
208
|
+
- Cross-file dependencies
|
|
209
|
+
- Recommended conversion order
|
|
210
|
+
""",
|
|
211
|
+
llm=self.llm,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
# Converter Agent
|
|
215
|
+
converter = Agent(
|
|
216
|
+
name="Converter",
|
|
217
|
+
role="Code Converter",
|
|
218
|
+
goal="Convert source code to PraisonAI format",
|
|
219
|
+
instructions=f"""You are a code conversion expert. Convert the provided source code to PraisonAI format.
|
|
220
|
+
|
|
221
|
+
Use this specification for conversion:
|
|
222
|
+
{spec_prompt}
|
|
223
|
+
|
|
224
|
+
Reference for PraisonAI code:
|
|
225
|
+
{PRAISONAI_REFERENCE}
|
|
226
|
+
|
|
227
|
+
Rules:
|
|
228
|
+
1. Preserve all logic and behavior
|
|
229
|
+
2. Convert imports to praisonaiagents
|
|
230
|
+
3. Map parameters according to the spec
|
|
231
|
+
4. Handle cross-file dependencies correctly
|
|
232
|
+
5. Ensure the converted code is syntactically valid Python
|
|
233
|
+
6. Do NOT include any framework names in comments
|
|
234
|
+
|
|
235
|
+
Output the complete converted code for each file.
|
|
236
|
+
""",
|
|
237
|
+
llm=self.llm,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
# Evaluator Agent (LLM-as-Judge)
|
|
241
|
+
evaluator = Agent(
|
|
242
|
+
name="Evaluator",
|
|
243
|
+
role="Migration Evaluator",
|
|
244
|
+
goal="Evaluate the quality of code migration",
|
|
245
|
+
instructions="""You are a code quality evaluator using LLM-as-Judge methodology.
|
|
246
|
+
|
|
247
|
+
Evaluate the converted code on these criteria (score 1-10 each):
|
|
248
|
+
1. **Syntax Correctness**: Is the code valid Python?
|
|
249
|
+
2. **Import Completeness**: Are all necessary imports present?
|
|
250
|
+
3. **Functional Equivalence**: Does it preserve the original behavior?
|
|
251
|
+
4. **Pattern Accuracy**: Are agent patterns correctly converted?
|
|
252
|
+
5. **Dependency Handling**: Are cross-file dependencies handled?
|
|
253
|
+
|
|
254
|
+
Output format:
|
|
255
|
+
```json
|
|
256
|
+
{
|
|
257
|
+
"scores": {
|
|
258
|
+
"syntax": 8,
|
|
259
|
+
"imports": 9,
|
|
260
|
+
"equivalence": 7,
|
|
261
|
+
"patterns": 8,
|
|
262
|
+
"dependencies": 9
|
|
263
|
+
},
|
|
264
|
+
"overall_score": 8.2,
|
|
265
|
+
"passed": true,
|
|
266
|
+
"issues": ["list of specific issues if any"],
|
|
267
|
+
"suggestions": ["list of improvements if any"]
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
A score of 8.0 or higher means PASSED.
|
|
272
|
+
""",
|
|
273
|
+
llm=self.llm,
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
# Error Agent
|
|
277
|
+
error_agent = Agent(
|
|
278
|
+
name="ErrorAnalyzer",
|
|
279
|
+
role="Error Analyzer",
|
|
280
|
+
goal="Analyze migration errors and suggest fixes",
|
|
281
|
+
instructions="""You are an error analysis expert. When migration evaluation fails:
|
|
282
|
+
|
|
283
|
+
1. Analyze the specific errors reported
|
|
284
|
+
2. Identify the root cause
|
|
285
|
+
3. Suggest specific fixes
|
|
286
|
+
4. Provide corrected code snippets
|
|
287
|
+
|
|
288
|
+
Output format:
|
|
289
|
+
```json
|
|
290
|
+
{
|
|
291
|
+
"root_cause": "description of the main issue",
|
|
292
|
+
"affected_files": ["list of files with issues"],
|
|
293
|
+
"fixes": [
|
|
294
|
+
{
|
|
295
|
+
"file": "filename",
|
|
296
|
+
"issue": "what's wrong",
|
|
297
|
+
"fix": "how to fix it",
|
|
298
|
+
"corrected_code": "the fixed code snippet"
|
|
299
|
+
}
|
|
300
|
+
],
|
|
301
|
+
"retry_recommended": true
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
""",
|
|
305
|
+
llm=self.llm,
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
return analyzer, converter, evaluator, error_agent
|
|
309
|
+
|
|
310
|
+
def run(self) -> MigrationResult:
|
|
311
|
+
"""Run the migration flow."""
|
|
312
|
+
result = MigrationResult(success=False)
|
|
313
|
+
|
|
314
|
+
# Gather all files
|
|
315
|
+
logger.info(f"Gathering files from {self.source_path}")
|
|
316
|
+
self._context = self._gather_files()
|
|
317
|
+
|
|
318
|
+
if not self._context.files:
|
|
319
|
+
result.errors.append("No Python files found")
|
|
320
|
+
return result
|
|
321
|
+
|
|
322
|
+
logger.info(f"Found {len(self._context.files)} files to analyze")
|
|
323
|
+
|
|
324
|
+
# Create agents
|
|
325
|
+
analyzer, converter, evaluator, error_agent = self._create_agents()
|
|
326
|
+
|
|
327
|
+
# Build the combined context for all files
|
|
328
|
+
all_code = []
|
|
329
|
+
for path, file_ctx in self._context.files.items():
|
|
330
|
+
all_code.append(f"# File: {path}\n{file_ctx.content}")
|
|
331
|
+
combined_code = "\n\n---\n\n".join(all_code)
|
|
332
|
+
|
|
333
|
+
# Retry loop
|
|
334
|
+
for iteration in range(self.max_retries):
|
|
335
|
+
result.iterations = iteration + 1
|
|
336
|
+
logger.info(f"Migration iteration {iteration + 1}/{self.max_retries}")
|
|
337
|
+
|
|
338
|
+
try:
|
|
339
|
+
# Step 1: Analyze
|
|
340
|
+
analysis_prompt = f"""Analyze this codebase for migration:
|
|
341
|
+
|
|
342
|
+
{self._context.to_prompt()}
|
|
343
|
+
|
|
344
|
+
Full code:
|
|
345
|
+
{combined_code}
|
|
346
|
+
"""
|
|
347
|
+
analysis = analyzer.start(analysis_prompt)
|
|
348
|
+
logger.info("Analysis complete")
|
|
349
|
+
|
|
350
|
+
# Step 2: Convert
|
|
351
|
+
convert_prompt = f"""Convert this code to PraisonAI format.
|
|
352
|
+
|
|
353
|
+
Analysis results:
|
|
354
|
+
{analysis}
|
|
355
|
+
|
|
356
|
+
Original code:
|
|
357
|
+
{combined_code}
|
|
358
|
+
|
|
359
|
+
Convert ALL files and output the complete converted code for each file.
|
|
360
|
+
Format: Start each file with "# FILE: filename.py" followed by the converted code.
|
|
361
|
+
"""
|
|
362
|
+
converted = converter.start(convert_prompt)
|
|
363
|
+
logger.info("Conversion complete")
|
|
364
|
+
|
|
365
|
+
# Parse converted files
|
|
366
|
+
self._parse_converted_output(converted, result)
|
|
367
|
+
|
|
368
|
+
# Step 3: Evaluate
|
|
369
|
+
eval_prompt = f"""Evaluate this migration:
|
|
370
|
+
|
|
371
|
+
Original code:
|
|
372
|
+
{combined_code}
|
|
373
|
+
|
|
374
|
+
Converted code:
|
|
375
|
+
{converted}
|
|
376
|
+
|
|
377
|
+
Evaluate and score the migration quality.
|
|
378
|
+
"""
|
|
379
|
+
evaluation = evaluator.start(eval_prompt)
|
|
380
|
+
logger.info("Evaluation complete")
|
|
381
|
+
|
|
382
|
+
# Parse evaluation result
|
|
383
|
+
import json
|
|
384
|
+
import re
|
|
385
|
+
|
|
386
|
+
# Try to extract JSON from evaluation
|
|
387
|
+
json_match = re.search(r'\{[\s\S]*\}', evaluation)
|
|
388
|
+
if json_match:
|
|
389
|
+
try:
|
|
390
|
+
eval_result = json.loads(json_match.group())
|
|
391
|
+
result.evaluation_score = eval_result.get('overall_score', 0)
|
|
392
|
+
|
|
393
|
+
if eval_result.get('passed', False) or result.evaluation_score >= 8.0:
|
|
394
|
+
result.success = True
|
|
395
|
+
result.files_converted = len(result.converted_files)
|
|
396
|
+
logger.info(f"Migration PASSED with score {result.evaluation_score}")
|
|
397
|
+
break
|
|
398
|
+
else:
|
|
399
|
+
# Evaluation failed, run error analysis
|
|
400
|
+
issues = eval_result.get('issues', [])
|
|
401
|
+
result.warnings.extend(issues)
|
|
402
|
+
|
|
403
|
+
if iteration < self.max_retries - 1:
|
|
404
|
+
error_prompt = f"""Migration evaluation failed.
|
|
405
|
+
|
|
406
|
+
Issues found:
|
|
407
|
+
{issues}
|
|
408
|
+
|
|
409
|
+
Converted code:
|
|
410
|
+
{converted}
|
|
411
|
+
|
|
412
|
+
Analyze the errors and suggest fixes.
|
|
413
|
+
"""
|
|
414
|
+
_ = error_agent.start(error_prompt)
|
|
415
|
+
logger.info("Error analysis complete, retrying...")
|
|
416
|
+
# The next iteration will use the error analysis context
|
|
417
|
+
except json.JSONDecodeError:
|
|
418
|
+
result.warnings.append("Could not parse evaluation result")
|
|
419
|
+
else:
|
|
420
|
+
result.warnings.append("No evaluation JSON found")
|
|
421
|
+
|
|
422
|
+
except Exception as e:
|
|
423
|
+
result.errors.append(f"Iteration {iteration + 1} failed: {str(e)}")
|
|
424
|
+
logger.error(f"Migration error: {e}")
|
|
425
|
+
|
|
426
|
+
if not result.success:
|
|
427
|
+
result.errors.append(f"Migration failed after {self.max_retries} attempts")
|
|
428
|
+
|
|
429
|
+
# Save converted files if not dry run
|
|
430
|
+
if not self.dry_run and result.success:
|
|
431
|
+
self._save_converted_files(result)
|
|
432
|
+
|
|
433
|
+
return result
|
|
434
|
+
|
|
435
|
+
def _parse_converted_output(self, converted: str, result: MigrationResult) -> None:
|
|
436
|
+
"""Parse the converted output into individual files."""
|
|
437
|
+
import re
|
|
438
|
+
|
|
439
|
+
# Split by file markers
|
|
440
|
+
file_pattern = r'#\s*FILE:\s*(\S+\.py)'
|
|
441
|
+
parts = re.split(file_pattern, converted)
|
|
442
|
+
|
|
443
|
+
# parts[0] is before first file, then alternating filename, content
|
|
444
|
+
for i in range(1, len(parts), 2):
|
|
445
|
+
if i + 1 < len(parts):
|
|
446
|
+
filename = parts[i].strip()
|
|
447
|
+
content = parts[i + 1].strip()
|
|
448
|
+
# Clean up code blocks
|
|
449
|
+
content = re.sub(r'^```python\s*', '', content)
|
|
450
|
+
content = re.sub(r'\s*```$', '', content)
|
|
451
|
+
result.converted_files[filename] = content
|
|
452
|
+
|
|
453
|
+
def _save_converted_files(self, result: MigrationResult) -> None:
|
|
454
|
+
"""Save converted files to output directory."""
|
|
455
|
+
output_dir = Path(self.output_path)
|
|
456
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
457
|
+
|
|
458
|
+
for filename, content in result.converted_files.items():
|
|
459
|
+
filepath = output_dir / filename
|
|
460
|
+
filepath.parent.mkdir(parents=True, exist_ok=True)
|
|
461
|
+
with open(filepath, 'w', encoding='utf-8') as f:
|
|
462
|
+
f.write(content)
|
|
463
|
+
logger.info(f"Saved: {filepath}")
|