Skip to content

Commit a9d96ae

Browse files
Yuwei YanOmX
andcommitted
v0.2.4 support pip-installed skill defaults
Package built-in EasyPaper skill YAMLs for pip users, synchronize the Claude Code configuration template with the project example, and avoid requiring FastAPI for SDK-only imports. Constraint: pip-installed users must be able to enable skills without cloning the repository skills directory. Constraint: Claude Code plugin configuration should match configs/example.yaml. Rejected: Add FastAPI to base dependencies | SDK-only users should not pay for optional server dependencies. Confidence: high Scope-risk: moderate Directive: Keep root skills/ and src/skills/builtin synchronized before release packaging. Tested: pytest tests -q (1043 passed, 9 skipped) Tested: clean venv local pip install without fastapi, SDK import and EasyPaper config init Tested: targeted router and SDK regression tests; compileall; git diff --check; ruff F821 on affected files Co-authored-by: OmX <omx@oh-my-codex.dev>
1 parent de3d5c0 commit a9d96ae

51 files changed

Lines changed: 1874 additions & 160 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude-plugin/README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,12 @@ This will guide you through an interactive workflow to generate your academic pa
3535

3636
## Configuration
3737

38-
Create a YAML config file (see `configs/example.yaml` in the project) with your API keys:
38+
If you do not already have a config, run `/easypaper-setup`. The setup skill
39+
creates `easypaper_config.yaml` from its bundled `config.example.yaml`, which is
40+
kept synchronized with the current EasyPaper `configs/example.yaml` template.
41+
You only need to replace the API key placeholders.
42+
43+
For reference, a minimal agent entry looks like:
3944

4045
```yaml
4146
agents:

configs/example.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ skills:
66
- '*'
77
# venue_profile: "neurips"
88

9-
# Tools configuration for ReAct-enabled agents (MetaDataAgent, WriterAgent)
9+
# Tools configuration for ReAct-enabled agents (MetaDataAgent, WriterAgent)
1010
tools:
1111
enabled: true
1212
planner_structure_signals_enabled: true # Planner emits soft structure signals

easypaper/client.py

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
from typing import Any, AsyncGenerator, Dict, Optional
2929

3030
from src.config.loader import load_config
31-
from src.config.schema import AppConfig, SkillsConfig
31+
from src.config.schema import AppConfig
32+
from src.skills.bootstrap import bootstrap_skill_registry_for_config, format_skill_load_report
3233
from src.agents import initialize_agents
3334
from src.agents.shared.docling_service import DoclingService
3435
from src.agents.shared.docling_analyzer import DoclingPaperResult
@@ -281,24 +282,15 @@ def _build_agents(config: AppConfig) -> Dict[str, Any]:
281282
- **Returns**:
282283
- `Dict[str, BaseAgent]`: Agent name -> instance mapping.
283284
"""
284-
skill_registry = None
285-
skills_config = config.skills or SkillsConfig(enabled=False)
286-
if skills_config.enabled:
287-
try:
288-
from src.skills.loader import SkillLoader
289-
from src.skills.registry import SkillRegistry
290-
skill_registry = SkillRegistry()
291-
loader = SkillLoader()
292-
for skill in loader.load_directory(Path(skills_config.skills_dir)):
293-
skill_registry.register(skill)
294-
logger.info("Skills: loaded %d skills", len(skill_registry))
295-
except Exception:
296-
logger.warning("Skills loading failed; continuing without skills")
297-
skill_registry = None
285+
skill_registry, skills_config, skill_report = bootstrap_skill_registry_for_config(
286+
config.skills
287+
)
288+
logger.info("Skills: %s", format_skill_load_report(skill_report))
298289

299290
agents = initialize_agents(
300291
config.agents,
301292
skill_registry=skill_registry,
293+
skills_config=skills_config if skill_registry is not None else None,
302294
global_tools_config=config.tools,
303295
vlm_service_config=config.vlm_service,
304296
)

plugins/easypaper/.claude-plugin/README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,12 @@ pip install easypaper
6262

6363
## Configuration
6464

65-
Create a YAML config file (see `configs/example.yaml` in the project) with your API keys:
65+
If you do not already have a config, run `/easypaper-setup`. The setup skill
66+
creates `easypaper_config.yaml` from its bundled `config.example.yaml`, which is
67+
kept synchronized with the current EasyPaper `configs/example.yaml` template.
68+
You only need to replace the API key placeholders.
69+
70+
For reference, a minimal agent entry looks like:
6671

6772
```yaml
6873
agents:

plugins/easypaper/commands/easypaper-paper-from-metadata.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ options = {
5959
"artifacts_prefix": request.artifacts_prefix or "",
6060
}
6161

62-
result = await EasyPaper(config_path=str(Path("configs/openrouter.yaml").resolve())).generate(
62+
result = await EasyPaper(config_path=str(Path("easypaper_config.yaml").resolve())).generate(
6363
metadata,
6464
**options,
6565
)
@@ -86,7 +86,9 @@ result = await EasyPaper(config_path=str(Path("configs/openrouter.yaml").resolve
8686
- Missing required fields: collect missing information interactively.
8787
- Invalid metadata format: show validation errors and reference `examples/meta.json`.
8888
- Package not installed: use `easypaper-setup-environment`.
89-
- Config missing: ask for config path or use the default config path if present.
89+
- Config missing: use `easypaper-setup-environment` to create
90+
`easypaper_config.yaml` from the synchronized skill-bundled template, or ask
91+
for an explicit config path.
9092
- Final PDF selection: prefer `result.pdf_path`, then `iteration_*_final/**/*.pdf`, latest `iteration_*`, then `paper.pdf`.
9193

9294
$ARGUMENTS

plugins/easypaper/commands/easypaper-setup.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@ Set up EasyPaper environment including Python dependencies and LaTeX toolchain.
55
1. Use the `easypaper-setup-environment` skill to:
66
- Create isolated virtual environment (prefer `uv`, fallback to `venv`)
77
- Install easypaper package
8+
- Create or validate `easypaper_config.yaml` from the skill-bundled
9+
`config.example.yaml` template when the user does not already have config
810
- Check LaTeX installation and provide installation instructions if missing
911
- Verify all components are working
1012

1113
2. After setup, provide clear instructions on:
1214
- How to activate the environment
1315
- How to use EasyPaper as Python SDK: `from easypaper import EasyPaper, PaperMetaData`
14-
- Next steps for using the plugin (need config file with API keys)
16+
- Which config path will be used for generation
17+
- Next steps for using the plugin
1518

1619
$ARGUMENTS

plugins/easypaper/commands/easypaper.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ Run the EasyPaper end-to-end paper generation workflow with guided setup and met
66

77
1. Check whether EasyPaper is usable:
88
- `.easypaper-env` exists, or `python -c "import easypaper"` succeeds
9+
- A usable EasyPaper config exists at a user-provided path, `AGENT_CONFIG_PATH`,
10+
`./easypaper_config.yaml`, `./configs/openrouter.yaml`, or `./configs/example.yaml`
911
- `pdflatex` is available when the user wants PDF output
10-
2. If not ready, use the `easypaper-setup-environment` skill to create an isolated environment, install EasyPaper, and guide LaTeX setup.
12+
2. If not ready, use the `easypaper-setup-environment` skill to create an isolated environment, install EasyPaper, create/validate configuration from the synchronized template, and guide LaTeX setup.
1113

1214
### Phase 2: Choose metadata source
1315

@@ -40,7 +42,7 @@ import json
4042
from pathlib import Path
4143
from easypaper import EasyPaper, PaperGenerationRequest
4244

43-
config_path = Path("configs/openrouter.yaml").resolve()
45+
config_path = Path("easypaper_config.yaml").resolve()
4446
request = PaperGenerationRequest.model_validate(
4547
json.loads(Path("metadata.json").read_text(encoding="utf-8"))
4648
)

plugins/easypaper/skills/easypaper-paper-from-metadata/SKILL.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ options = {
5252
"artifacts_prefix": request.artifacts_prefix or "",
5353
}
5454

55-
ep = EasyPaper(config_path=str(Path("configs/openrouter.yaml").resolve()))
55+
ep = EasyPaper(config_path=str(Path("easypaper_config.yaml").resolve()))
5656
result = await ep.generate(paper_metadata, **options)
5757
```
5858

@@ -149,6 +149,9 @@ Folder-generated metadata:
149149

150150
Config:
151151
- Resolve `config_path` before constructing `EasyPaper`.
152+
- Prefer the setup-generated `./easypaper_config.yaml`. If it is missing,
153+
run `easypaper-setup-environment` so Claude can create it from the
154+
synchronized skill-bundled `config.example.yaml` template.
152155

153156
## Alternative: Generate Metadata from a Materials Folder
154157

@@ -158,7 +161,7 @@ When the user has a folder of research materials instead of a ready metadata JSO
158161
from pathlib import Path
159162
from easypaper import EasyPaper
160163

161-
ep = EasyPaper(config_path=str(Path("configs/openrouter.yaml").resolve()))
164+
ep = EasyPaper(config_path=str(Path("easypaper_config.yaml").resolve()))
162165

163166
metadata = await ep.generate_metadata_from_folder(
164167
str(Path("path/to/materials").resolve()),

plugins/easypaper/skills/easypaper-setup-environment/SKILL.md

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,23 +39,52 @@ Use this skill when the plugin is first installed or when environment setup is n
3939
- Verify installation by running: `pdflatex --version`
4040
- If not installed, provide clear installation instructions for the user's OS
4141

42-
### 4. Verify Setup
42+
### 4. Create or Validate Configuration
43+
44+
- Check for an existing EasyPaper YAML config in this order:
45+
- User-provided path in the conversation
46+
- `AGENT_CONFIG_PATH`
47+
- `./easypaper_config.yaml`
48+
- `./configs/openrouter.yaml`
49+
- `./configs/example.yaml`
50+
- If no usable config exists, create `./easypaper_config.yaml` from the bundled
51+
sibling template file `config.example.yaml`.
52+
- The bundled `config.example.yaml` must stay byte-for-byte synchronized with
53+
the repository source template `configs/example.yaml`; do not hand-write a
54+
partial config in this skill.
55+
- After creating the file, ask the user for the required LLM API key/provider
56+
value and replace only the placeholder values they explicitly provide
57+
(for example `YOUR_OPENROUTER_KEY`). Do not invent credentials.
58+
- Keep the generated config path explicit for later SDK calls, either by using
59+
`EasyPaper(config_path="<absolute path to easypaper_config.yaml>")` or by
60+
setting `AGENT_CONFIG_PATH` for commands that call `load_config()`.
61+
- The template enables the skills system. Users do not need a local `./skills`
62+
folder: EasyPaper loads packaged built-in skills first, then overlays user
63+
skills from `skills_dir` when that directory exists.
64+
65+
### 5. Verify Setup
4366

4467
- Test EasyPaper import: `python -c "import easypaper; from easypaper import EasyPaper, PaperMetaData; print('EasyPaper imported successfully')"`
4568
- Test LaTeX: `pdflatex --version`
69+
- Test config parsing when a config path exists:
70+
`python -c "from easypaper import EasyPaper; EasyPaper(config_path='easypaper_config.yaml'); print('EasyPaper config loaded successfully')"`
4671

47-
### 5. Create Environment Activation Script
72+
### 6. Create Environment Activation Script
4873

4974
Create a helper script (`.easypaper-activate.sh` or `.easypaper-activate.bat`) that:
5075
- Activates the virtual environment
5176
- Sets up PATH if needed
5277
- Provides instructions for using EasyPaper as Python SDK
78+
- Exports `AGENT_CONFIG_PATH` to the generated config path when practical
5379

5480
## Error Handling
5581

5682
- If Python version < 3.11, inform user and suggest upgrade
5783
- If package installation fails, show error and suggest manual installation
5884
- If LaTeX is missing, provide OS-specific installation commands
85+
- If the config template file is missing from the installed skill directory,
86+
report the plugin installation as incomplete and ask the user to reinstall
87+
the EasyPaper Claude Code plugin.
5988
- Always verify each step before proceeding to the next
6089

6190
## Post-Setup Instructions
@@ -65,4 +94,4 @@ After successful setup, inform the user:
6594
2. To activate: `source .easypaper-env/bin/activate` (or equivalent for Windows)
6695
3. EasyPaper can now be used directly as a Python SDK: `from easypaper import EasyPaper, PaperMetaData`
6796
4. The environment is isolated and won't affect system Python
68-
5. Make sure you have a config file (YAML) with your API keys - see `configs/example.yaml` for reference
97+
5. The active config file path, normally `./easypaper_config.yaml`
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# Skills system configuration
2+
skills:
3+
enabled: true
4+
skills_dir: ./skills
5+
active_skills:
6+
- '*'
7+
# venue_profile: "neurips"
8+
9+
# Tools configuration for ReAct-enabled agents (MetaDataAgent, WriterAgent)
10+
tools:
11+
enabled: true
12+
planner_structure_signals_enabled: true # Planner emits soft structure signals
13+
table_critic_enabled: true # Enable critic-gated table schema restructuring when justified
14+
table_critic_max_iterations: 2 # Max critic/rewrite rounds for table restructuring
15+
table_rendered_review_enabled: true # Run final rendered VLM/layout review for table formatting
16+
writer_structure_contract_enabled: true # Inject structure quality contract into Writer prompts
17+
review_structure_gate_enabled: true # Enforce structure quality gate in Reviewer
18+
structure_gate_min_paragraph_threshold: 5 # Trigger gate for dense sections
19+
available_tools:
20+
- validate_citations
21+
- count_words
22+
- check_key_points
23+
- search_papers
24+
max_react_iterations: 3
25+
paper_search:
26+
# Optional API keys. Leave null/omitted to use anonymous/fallback search.
27+
# semantic_scholar_api_key improves Semantic Scholar rate limits.
28+
semantic_scholar_api_key: null
29+
timeout: 15
30+
search_results_per_round: 12 # Papers to return per search round
31+
planner_max_queries_per_section: 5 # Max query rounds per section
32+
planner_inter_round_delay_sec: 1.5 # Delay between rounds to reduce 429
33+
planner_min_target_papers_per_section: 3 # Minimum discovered refs per section
34+
semantic_scholar_min_results_before_fallback: 3 # Fallback to arXiv when SS results are low
35+
enable_query_cache: true # Reuse repeated query results during service runtime
36+
cache_ttl_hours: 24 # Cache TTL for repeated queries
37+
citation_budget_enabled: true # Let planner decide per-section citation budgets
38+
citation_budget_soft_cap: true # Prefer budgeted refs, allow small overflow
39+
citation_budget_export: true # Export budget plan/usage artifacts
40+
citation_budget_reserve_size: 4 # Overflow reserve keys per section
41+
planner_landscape_max_queries: 8 # Pre-plan broad literature search for research context
42+
planner_max_utility_searches: 12 # Dataset / metric / framework triggers after plan
43+
research_context:
44+
enabled: true # Generate research context summary
45+
detailed: true # Detailed mode with trends and gaps
46+
top_k_key_papers: 10 # Number of key papers to include (also caps landscape pool for context LLM)
47+
claim_evidence_enabled: true # Generate claim-to-evidence mappings
48+
contribution_ranking_enabled: true # Generate P0/P1/P2 contribution priorities
49+
export_planning_decision_trace: false # Export explicit planning decision trace (debug)
50+
core_ref_analysis:
51+
enabled: true # LLM deep analysis of user core refs; false uses heuristic fallback only
52+
max_abstract_chars: 2000
53+
analyze_cross_paper: true
54+
docling:
55+
enabled: true
56+
device: auto
57+
do_ocr: false
58+
do_table_structure: true
59+
do_formula_enrichment: false
60+
max_pages: 30
61+
download_timeout: 30
62+
cleanup_after_analysis: true
63+
exemplar:
64+
enabled: true
65+
prefer_core_refs: true
66+
venue_match_required: true
67+
recency_years: 5
68+
max_external_candidates: 10
69+
max_analysis_chars: 8000
70+
71+
agents:
72+
- name: paper_parser
73+
model:
74+
model_name: google/gemini-3-flash-preview
75+
api_key: YOUR_OPENROUTER_KEY
76+
base_url: https://openrouter.ai/api/v1
77+
78+
- name: template_parser
79+
model:
80+
model_name: google/gemini-3-flash-preview
81+
api_key: YOUR_OPENROUTER_KEY
82+
base_url: https://openrouter.ai/api/v1
83+
84+
- name: commander
85+
model:
86+
model_name: google/gemini-3-flash-preview
87+
api_key: YOUR_OPENROUTER_KEY
88+
base_url: https://openrouter.ai/api/v1
89+
90+
- name: writer
91+
model:
92+
model_name: google/gemini-3-flash-preview
93+
api_key: YOUR_OPENROUTER_KEY
94+
base_url: https://openrouter.ai/api/v1
95+
writer_config:
96+
max_review_iterations: 2
97+
enable_review: true
98+
enable_tools: true
99+
available_tools:
100+
- validate_citations
101+
- count_words
102+
- check_key_points
103+
104+
- name: typesetter
105+
model:
106+
model_name: google/gemini-3-flash-preview
107+
api_key: YOUR_OPENROUTER_KEY
108+
base_url: https://openrouter.ai/api/v1
109+
110+
- name: metadata
111+
model:
112+
model_name: google/gemini-3-flash-preview
113+
api_key: YOUR_OPENROUTER_KEY
114+
base_url: https://openrouter.ai/api/v1
115+
metadata_config:
116+
enable_mini_review: true
117+
max_review_iterations: 2
118+
119+
- name: reviewer
120+
model:
121+
model_name: google/gemini-3-flash-preview
122+
api_key: YOUR_OPENROUTER_KEY
123+
base_url: https://openrouter.ai/api/v1
124+
125+
- name: planner
126+
model:
127+
model_name: google/gemini-3-flash-preview
128+
api_key: YOUR_OPENROUTER_KEY
129+
base_url: https://openrouter.ai/api/v1
130+
131+
- name: vlm_review
132+
vlm_review_config:
133+
enabled: true
134+
render_dpi: 150
135+
max_pages_to_analyze: 12
136+
check_overflow: true
137+
check_underfill: true
138+
check_layout: true
139+
min_fill_percentage: 0.85
140+
max_blank_area: 0.15
141+
142+
# Shared VLM service (PlannerAgent for figure/table analysis, VLMReviewAgent for PDF page analysis)
143+
vlm_service:
144+
enabled: true
145+
provider: openai
146+
model: openai/gpt-4o
147+
api_key: YOUR_OPENROUTER_KEY
148+
base_url: https://openrouter.ai/api/v1

0 commit comments

Comments
 (0)