jcmiao commited on
Commit
51f80a0
·
verified ·
1 Parent(s): bc29be4

Upload 18 files

Browse files
Files changed (4) hide show
  1. README.md +3 -3
  2. requirements.txt +12 -0
  3. test.py +143 -0
  4. utils.py +91 -0
README.md CHANGED
@@ -1,8 +1,8 @@
1
  ---
2
  title: Paper2Agent
3
- emoji: 🌍
4
- colorFrom: indigo
5
- colorTo: indigo
6
  sdk: gradio
7
  sdk_version: 6.0.2
8
  app_file: app.py
 
1
  ---
2
  title: Paper2Agent
3
+ emoji: 📈
4
+ colorFrom: yellow
5
+ colorTo: pink
6
  sdk: gradio
7
  sdk_version: 6.0.2
8
  app_file: app.py
requirements.txt ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Web UI
2
+ gradio
3
+
4
+ # Async runtime
5
+ anyio
6
+
7
+ # Claude Agent SDK
8
+ claude-agent-sdk
9
+
10
+ uv
11
+
12
+ fastmcp
test.py ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import anyio
2
+ import argparse
3
+ import shutil
4
+ import subprocess
5
+ from claude_agent_sdk import (
6
+ ClaudeSDKClient,
7
+ ClaudeAgentOptions,
8
+ AgentDefinition,
9
+ AssistantMessage,
10
+ TextBlock,
11
+ ToolUseBlock,
12
+ ToolResultBlock
13
+ )
14
+
15
+ import os
16
+ import sys
17
+ from pathlib import Path
18
+ from utils import copy_project_resources, clone_github_repo, prepare_folder_structure
19
+ from prompts.tasks import step1_environment_setup_and_tutorial_discovery, step2_tutorial_execution, step3_tool_extraction_and_testing, step4_mcp_integration
20
+
21
+ # ANTHROPIC_API_KEY should be set via environment variable by the caller (app.py)
22
+
23
+ async def fully_automatic(tasks: list, task_descriptions: list = None, log_file_path: str = None):
24
+ options = ClaudeAgentOptions(
25
+ allowed_tools=["Bash", "Edit", "Glob", "Grep", "NotebookEdit", "NotebookRead", "Read", "SlashCommand", "Task", "TodoWrite", "WebFetch", "WebSearch", "Write"],
26
+ permission_mode='acceptEdits',
27
+ cwd=str(Path.cwd()),
28
+ setting_sources=["project"],
29
+ )
30
+
31
+ async with ClaudeSDKClient(options=options) as client:
32
+ for i, task in enumerate(tasks, 1):
33
+ # Simple print for UI
34
+ print(f"\n{'='*70}")
35
+ if task_descriptions and i <= len(task_descriptions):
36
+ print(f"🚀 Starting {task_descriptions[i-1]}")
37
+ else:
38
+ print(f"🚀 Starting Task {i}")
39
+ print('='*70 + "\n")
40
+
41
+ try:
42
+ await client.query(task)
43
+ async for message in client.receive_response():
44
+ # Write detailed logs to file
45
+ if log_file_path:
46
+ with open(f"Task_{i}_{log_file_path}", 'a', encoding='utf-8') as log_file:
47
+ if isinstance(message, AssistantMessage):
48
+ for block in message.content:
49
+ if isinstance(block, TextBlock):
50
+ log_file.write(f"💭 Claude: {block.text}\n")
51
+ elif isinstance(block, ToolUseBlock):
52
+ if hasattr(block, 'input') and block.input:
53
+ if isinstance(block.input, dict):
54
+ for key, value in block.input.items():
55
+ val_str = str(value)
56
+ log_file.write(f"[ToolUseBlock] {key}: {val_str}\n")
57
+ elif isinstance(message, ToolResultBlock):
58
+ if hasattr(message, 'content'):
59
+ result = str(message.content)
60
+ log_file.write(f" ✅ Result: {result}\n")
61
+
62
+ # Only print brief progress to stdout for UI
63
+ if isinstance(message, AssistantMessage):
64
+ for block in message.content:
65
+ if isinstance(block, TextBlock):
66
+ # Only print short text blocks
67
+ text = block.text.strip()
68
+ if len(text) < 150:
69
+ print(f"💭 {text}")
70
+
71
+ print(f"\n✅ Task {i} Completed\n")
72
+
73
+ except Exception as e:
74
+ print(f"❌ Task {i} Failed: {e}\n")
75
+ if log_file_path:
76
+ with open(log_file_path, 'a', encoding='utf-8') as log_file:
77
+ log_file.write(f"❌ Task {i} Failed: {e}\n")
78
+
79
+
80
+ def main():
81
+
82
+ parser = argparse.ArgumentParser(description="Script for running tasks with configurable options.")
83
+ parser.add_argument('--github_url', dest='github_repo_url', default="", help='GitHub repository URL')
84
+ parser.add_argument('--tutorials', dest='tutorial_filter', default="", help='Tutorial filter')
85
+ parser.add_argument('--api', dest='api_key', default="", help='API key')
86
+ args = parser.parse_args()
87
+
88
+ GITHUB_REPO_URL = args.github_repo_url
89
+ FOLDER_NAME = "Results"
90
+ TUTORIAL_FILTER = args.tutorial_filter
91
+ API_KEY = args.api_key
92
+
93
+ # Extract repo_name from the GITHUB_REPO_URL (strip .git suffix if present)
94
+ if GITHUB_REPO_URL:
95
+ repo_name = os.path.basename(GITHUB_REPO_URL)
96
+ if repo_name.endswith(".git"):
97
+ repo_name = repo_name[:-4]
98
+ else:
99
+ repo_name = ""
100
+
101
+
102
+ os.makedirs(FOLDER_NAME, exist_ok=True)
103
+
104
+ # step 1: copy .claude, templates, tools to the project directory
105
+ copy_project_resources(FOLDER_NAME)
106
+
107
+ # step 2: prepare the folder structure
108
+ prepare_folder_structure(FOLDER_NAME)
109
+
110
+ os.chdir(FOLDER_NAME)
111
+
112
+ # step 3: clone the github repository
113
+ clone_github_repo(GITHUB_REPO_URL, repo_name)
114
+
115
+ task_descriptions = [
116
+ "Task 1: Environment Setup and Tutorial Discovery",
117
+ "Task 2: Tutorial Execution",
118
+ "Task 3: Tool Extraction and Testing",
119
+ "Task 4: MCP Integration"
120
+ ]
121
+
122
+ tasks = [
123
+ step1_environment_setup_and_tutorial_discovery(repo_name,TUTORIAL_FILTER),
124
+ step2_tutorial_execution(repo_name,API_KEY),
125
+ step3_tool_extraction_and_testing(repo_name,API_KEY),
126
+ step4_mcp_integration(repo_name),
127
+ ]
128
+
129
+ print("\n" + "="*70)
130
+ print("📋 Pipeline Tasks:")
131
+ for i, desc in enumerate(task_descriptions, 1):
132
+ print(f" {i}. {desc}")
133
+ print("="*70 + "\n")
134
+
135
+ # Define log file path
136
+ log_file_path = "log.log"
137
+
138
+ #print(tasks[0])
139
+ anyio.run(fully_automatic, tasks, task_descriptions, log_file_path)
140
+
141
+
142
+ if __name__ == "__main__":
143
+ main()
utils.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import anyio
2
+ import argparse
3
+ import shutil
4
+ import subprocess
5
+ import os
6
+ import sys
7
+ from pathlib import Path
8
+ def copy_project_resources(FOLDER_NAME):
9
+ """
10
+ Step 1: Copy .claude, templates, tools to the project directory.
11
+ """
12
+ MAIN_DIR = FOLDER_NAME or "."
13
+ SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
14
+ copy_targets = [
15
+ (".claude", "01: .claude already exists or source missing"),
16
+ ("templates", "01: templates already exists or source missing"),
17
+ ("tools", "01: tools already exists or source missing"),
18
+ ]
19
+
20
+ for subdir, error_msg in copy_targets:
21
+ src = os.path.join(SCRIPT_DIR, subdir)
22
+ dst = os.path.join(MAIN_DIR, subdir)
23
+ if not os.path.isdir(dst) and os.path.isdir(src):
24
+ try:
25
+ shutil.copytree(src, dst)
26
+ except Exception as copy_exc:
27
+ print(f"01: failed to copy {subdir}: {copy_exc}", file=sys.stderr)
28
+ else:
29
+ print(error_msg, file=sys.stderr)
30
+
31
+ def clone_github_repo(GITHUB_REPO_URL, repo_name):
32
+ """
33
+ Step 3: Clone the GitHub repository into the repo directory.
34
+ """
35
+ REPO_PARENT = os.path.join(os.getcwd(), "repo")
36
+ REPO_DIR = os.path.join(REPO_PARENT, repo_name) if repo_name else ""
37
+ GIT_URL = GITHUB_REPO_URL
38
+
39
+ print(f"03: clone target={GIT_URL} into {REPO_DIR}", file=sys.stderr)
40
+
41
+ os.makedirs(REPO_PARENT, exist_ok=True)
42
+
43
+ if repo_name and os.path.isdir(REPO_DIR):
44
+ print("03: repo dir already exists: {}".format(REPO_DIR), file=sys.stderr)
45
+ elif repo_name and GIT_URL:
46
+ clone_attempts = [
47
+ (["git", "clone", "--recurse-submodules", GIT_URL, REPO_DIR], "02: cloned with submodules"),
48
+ (["git", "clone", "--depth=1", GIT_URL, REPO_DIR], "02: shallow clone ok"),
49
+ (["git", "clone", GIT_URL, REPO_DIR], None),
50
+ ]
51
+ for i, (cmd, success_msg) in enumerate(clone_attempts):
52
+ if i == 0:
53
+ print("03: starting git clone with submodules...", file=sys.stderr)
54
+ elif i == 1:
55
+ print("03: main clone failed, trying --depth=1", file=sys.stderr)
56
+ elif i == 2:
57
+ print("03: shallow clone failed, trying plain clone", file=sys.stderr)
58
+ try:
59
+ rc = subprocess.call(cmd)
60
+ except Exception as e:
61
+ print(f"03: clone exception: {e}", file=sys.stderr)
62
+ rc = 1
63
+ if rc == 0:
64
+ if success_msg:
65
+ print(success_msg, file=sys.stderr)
66
+ break
67
+ else:
68
+ print("03: failed: all clone attempts failed", file=sys.stderr)
69
+
70
+ def prepare_folder_structure(main_dir):
71
+ """
72
+ Prepare the required folder structure for the pipeline under main_dir.
73
+
74
+ Args:
75
+ main_dir (str): The main project directory where folders will be created.
76
+ """
77
+ print(f"02: preparing folder structure under {main_dir}", file=sys.stderr)
78
+ dirs_to_make = [
79
+ os.path.join(main_dir, "reports"),
80
+ os.path.join(main_dir, "src", "tools"),
81
+ os.path.join(main_dir, "tests", "code"),
82
+ os.path.join(main_dir, "tests", "data"),
83
+ os.path.join(main_dir, "notebooks"),
84
+ os.path.join(main_dir, "tests", "results"),
85
+ os.path.join(main_dir, "tests", "logs"),
86
+ os.path.join(main_dir, "tests", "summary"),
87
+ os.path.join(main_dir, "tmp", "inputs"),
88
+ os.path.join(main_dir, "tmp", "outputs"),
89
+ ]
90
+ for d in dirs_to_make:
91
+ os.makedirs(d, exist_ok=True)