claude-code-arcane 2.9.7 → 2.9.9
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.
- package/CHANGELOG.md +21 -0
- package/agents/business/business-ops-lead.md +1 -14
- package/agents/game/gameplay-programmer.md +3 -4
- package/agents/game/technical-artist.md +3 -4
- package/dist/cli.js +74 -24
- package/docs/USER-GUIDE.md +1 -1
- package/docs/coordination-rules.md +1 -1
- package/docs/directory-structure.md +93 -122
- package/package.json +1 -1
- package/profiles/backend-go.yaml +1 -0
- package/profiles/core.yaml +0 -1
- package/rules/gamedev/engine-code.md +1 -1
- package/rules/gamedev/shader-code.md +1 -2
- package/rules/gamedev/vn-code.md +3 -2
- package/rules/implementation-workflow.md +1 -1
- package/rules/{ai-code.md → llm-code.md} +5 -5
- package/skills/agent-designer/README.md +2 -1
- package/skills/agent-designer/agent_evaluator.py +0 -5
- package/skills/ai-sdk-setup/SKILL.md +4 -4
- package/skills/architecture-decision/SKILL.md +3 -4
- package/skills/architecture-review/SKILL.md +1 -1
- package/skills/asset-audit/SKILL.md +0 -1
- package/skills/balance-check/SKILL.md +1 -1
- package/skills/brainstorm/SKILL.md +3 -3
- package/skills/brainstorm/references/next-steps-pipeline.md +8 -9
- package/skills/consistency-check/SKILL.md +2 -2
- package/skills/design-system/SKILL.md +1 -1
- package/skills/design-system/references/post-design-validation.md +2 -3
- package/skills/frontend-dev/SKILL.md +27 -28
- package/skills/frontend-dev/references/asset-prompt-guide.md +1 -1
- package/skills/frontend-dev/references/motion-recipes.md +1 -1
- package/skills/help/SKILL.md +1 -2
- package/skills/hotfix/SKILL.md +2 -2
- package/skills/milestone-review/SKILL.md +1 -1
- package/skills/patch-notes/SKILL.md +1 -1
- package/skills/playtest-report/SKILL.md +2 -3
- package/skills/playwright-pro/CLAUDE.md +2 -2
- package/skills/playwright-pro/SKILL.md +13 -9
- package/skills/playwright-pro/agents/migration-planner.md +2 -2
- package/skills/playwright-pro/agents/test-architect.md +1 -2
- package/skills/playwright-pro/agents/test-debugger.md +1 -1
- package/skills/playwright-pro/hooks/detect-playwright.sh +1 -1
- package/skills/playwright-pro/skills/browserstack/SKILL.md +5 -5
- package/skills/playwright-pro/skills/migrate/SKILL.md +1 -1
- package/skills/playwright-pro/skills/report/SKILL.md +2 -2
- package/skills/playwright-pro/skills/testrail/SKILL.md +5 -5
- package/skills/playwright-pro/templates/README.md +2 -2
- package/skills/prototype/SKILL.md +1 -1
- package/skills/quick-design/SKILL.md +1 -1
- package/skills/release-checklist/SKILL.md +0 -1
- package/skills/security-audit/SKILL.md +1 -1
- package/skills/self-improving-agent/CLAUDE.md +12 -12
- package/skills/self-improving-agent/SKILL.md +16 -12
- package/skills/self-improving-agent/agents/memory-analyst.md +1 -1
- package/skills/self-improving-agent/hooks/error-capture.sh +2 -2
- package/skills/self-improving-agent/reference/memory-architecture.md +2 -2
- package/skills/self-improving-agent/reference/promotion-rules.md +3 -3
- package/skills/self-improving-agent/reference/rules-directory-patterns.md +1 -1
- package/skills/self-improving-agent/settings.json +5 -5
- package/skills/self-improving-agent/skills/extract/SKILL.md +7 -7
- package/skills/self-improving-agent/skills/promote/SKILL.md +5 -5
- package/skills/self-improving-agent/skills/remember/SKILL.md +9 -9
- package/skills/self-improving-agent/skills/review/SKILL.md +7 -7
- package/skills/self-improving-agent/skills/status/SKILL.md +8 -8
- package/skills/senior-ml-engineer/references/llm_integration_guide.md +14 -31
- package/skills/senior-prompt-engineer/SKILL.md +10 -10
- package/skills/senior-prompt-engineer/references/prompt_engineering_patterns.md +1 -1
- package/skills/senior-prompt-engineer/scripts/agent_orchestrator.py +43 -40
- package/skills/senior-prompt-engineer/scripts/prompt_optimizer.py +23 -66
- package/skills/shader-dev/reference/fluid-simulation.md +4 -4
- package/skills/shader-dev/reference/simulation-physics.md +6 -6
- package/skills/shader-dev/reference/voxel-rendering.md +1 -1
- package/skills/shader-dev/techniques/ambient-occlusion.md +1 -1
- package/skills/shader-dev/techniques/analytic-ray-tracing.md +2 -2
- package/skills/shader-dev/techniques/domain-warping.md +2 -2
- package/skills/shader-dev/techniques/fluid-simulation.md +45 -45
- package/skills/shader-dev/techniques/multipass-buffer.md +24 -24
- package/skills/shader-dev/techniques/normal-estimation.md +1 -1
- package/skills/shader-dev/techniques/particle-system.md +16 -16
- package/skills/shader-dev/techniques/polar-uv-manipulation.md +1 -1
- package/skills/shader-dev/techniques/post-processing.md +7 -7
- package/skills/shader-dev/techniques/sdf-3d.md +1 -1
- package/skills/shader-dev/techniques/simulation-physics.md +24 -24
- package/skills/shader-dev/techniques/sound-synthesis.md +2 -2
- package/skills/shader-dev/techniques/voronoi-cellular-noise.md +1 -1
- package/skills/shader-dev/techniques/voxel-rendering.md +60 -60
- package/skills/smoke-check/SKILL.md +1 -1
- package/skills/sprint-plan/SKILL.md +1 -2
- package/skills/start/SKILL.md +7 -7
- package/skills/story-done/SKILL.md +1 -1
- package/skills/test-helpers/SKILL.md +7 -2
- package/skills/test-setup/SKILL.md +1 -1
- package/skills/ue-actor-component-architecture/SKILL.md +0 -1
- package/skills/ue-ai-navigation/SKILL.md +0 -1
- package/skills/ue-animation-system/SKILL.md +0 -1
- package/skills/ue-async-threading/SKILL.md +0 -1
- package/skills/ue-audio-system/SKILL.md +0 -1
- package/skills/ue-blueprints/SKILL.md +0 -1
- package/skills/ue-character-movement/SKILL.md +0 -1
- package/skills/ue-cpp-foundations/SKILL.md +0 -1
- package/skills/ue-data-assets-tables/SKILL.md +0 -1
- package/skills/ue-editor-tools/SKILL.md +0 -1
- package/skills/ue-game-features/SKILL.md +0 -1
- package/skills/ue-gameplay-abilities/SKILL.md +0 -1
- package/skills/ue-gameplay-framework/SKILL.md +0 -1
- package/skills/ue-input-system/SKILL.md +0 -1
- package/skills/ue-mass-entity/SKILL.md +0 -1
- package/skills/ue-materials-rendering/SKILL.md +0 -1
- package/skills/ue-module-build-system/SKILL.md +0 -1
- package/skills/ue-naming-conventions/SKILL.md +0 -1
- package/skills/ue-networking-replication/SKILL.md +0 -1
- package/skills/ue-niagara-effects/SKILL.md +0 -1
- package/skills/ue-packaging/SKILL.md +0 -1
- package/skills/ue-physics-collision/SKILL.md +0 -1
- package/skills/ue-procedural-generation/SKILL.md +0 -1
- package/skills/ue-project-context/SKILL.md +0 -1
- package/skills/ue-sequencer-cinematics/SKILL.md +0 -1
- package/skills/ue-serialization-savegames/SKILL.md +0 -1
- package/skills/ue-state-trees/SKILL.md +0 -1
- package/skills/ue-testing-debugging/SKILL.md +0 -1
- package/skills/ue-ui-umg-slate/SKILL.md +0 -1
- package/skills/ue-world-level-streaming/SKILL.md +0 -1
- package/skills/ux-review/SKILL.md +1 -1
- package/docs/RELEASE-SETUP.md +0 -99
- package/docs/presentations/arcane-overview-12.pptx +0 -0
- package/docs/presentations/arcane-overview.pptx +0 -0
- package/docs/presentations/build_arcane_deck.py +0 -310
- package/docs/presentations/build_arcane_deck_12.py +0 -399
- package/docs/trusted-publishing-setup.md +0 -163
|
@@ -23,12 +23,12 @@ python scripts/agent_orchestrator.py agent_config.yaml --visualize # Visua
|
|
|
23
23
|
|
|
24
24
|
### 1. Prompt Optimizer
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
Static checks for clarity and structure, with an approximate token count (characters / 4). `--optimize` only normalizes whitespace; the real rewrite is yours.
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
29
|
python scripts/prompt_optimizer.py prompt.txt --analyze # Full analysis
|
|
30
|
-
python scripts/prompt_optimizer.py prompt.txt --optimize #
|
|
31
|
-
python scripts/prompt_optimizer.py prompt.txt --tokens
|
|
30
|
+
python scripts/prompt_optimizer.py prompt.txt --optimize # Normalize whitespace
|
|
31
|
+
python scripts/prompt_optimizer.py prompt.txt --tokens # Approximate token count
|
|
32
32
|
python scripts/prompt_optimizer.py prompt.txt --extract-examples --output examples.json
|
|
33
33
|
```
|
|
34
34
|
|
|
@@ -50,7 +50,7 @@ Parses agent definitions and visualizes execution flows. Validates tool configur
|
|
|
50
50
|
python scripts/agent_orchestrator.py agent.yaml --validate # Validate config
|
|
51
51
|
python scripts/agent_orchestrator.py agent.yaml --visualize # Show workflow (ASCII)
|
|
52
52
|
python scripts/agent_orchestrator.py agent.yaml --visualize --format mermaid # Mermaid
|
|
53
|
-
python scripts/agent_orchestrator.py agent.yaml --estimate-cost #
|
|
53
|
+
python scripts/agent_orchestrator.py agent.yaml --estimate-cost --price-in 4 --price-out 20 # Tokens; dollars with your current prices (USD/Mtok)
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
## Prompt Engineering Workflows
|
|
@@ -65,7 +65,7 @@ python scripts/agent_orchestrator.py agent.yaml --estimate-cost # Token estimat
|
|
|
65
65
|
|-------|---------|
|
|
66
66
|
| Ambiguous output | Add explicit format specification |
|
|
67
67
|
| Too verbose | Extract to few-shot examples |
|
|
68
|
-
| Inconsistent results | Add
|
|
68
|
+
| Inconsistent results | Add 2-3 varied examples and state the success criteria |
|
|
69
69
|
| Missing edge cases | Add constraint boundaries |
|
|
70
70
|
|
|
71
71
|
4. **Optimize**: `python scripts/prompt_optimizer.py current_prompt.txt --optimize --output optimized.txt`
|
|
@@ -77,15 +77,15 @@ python scripts/agent_orchestrator.py agent.yaml --estimate-cost # Token estimat
|
|
|
77
77
|
1. Define task clearly (input format, output format)
|
|
78
78
|
2. Select 3-5 diverse examples: simple case, edge case, complex case, negative case
|
|
79
79
|
3. Format consistently
|
|
80
|
-
4.
|
|
80
|
+
4. Check what the prompt contains: `python scripts/prompt_optimizer.py prompt_with_examples.txt --extract-examples`
|
|
81
81
|
5. Test with held-out cases
|
|
82
82
|
|
|
83
83
|
### Structured Output Design
|
|
84
84
|
|
|
85
85
|
1. Define JSON/XML schema with types, constraints, enums
|
|
86
|
-
2.
|
|
87
|
-
3.
|
|
88
|
-
4. Validate
|
|
86
|
+
2. Pass the schema through the API's structured outputs (`output_config.format` on Claude) so the response is guaranteed to match it; do not rely on "respond ONLY with valid JSON" in the prompt
|
|
87
|
+
3. Describe in the prompt what each field means, not the JSON syntax
|
|
88
|
+
4. Validate the parsed result against the schema in code before using it
|
|
89
89
|
|
|
90
90
|
## Reference Documentation
|
|
91
91
|
|
|
@@ -103,6 +103,6 @@ python scripts/agent_orchestrator.py agent.yaml --estimate-cost # Token estimat
|
|
|
103
103
|
|---------|-------------|---------|
|
|
104
104
|
| **Zero-shot** | Simple, well-defined tasks | "Classify this email as spam or not spam" |
|
|
105
105
|
| **Few-shot** | Complex tasks, consistent format | Provide 3-5 examples before task |
|
|
106
|
-
| **
|
|
106
|
+
| **Reasoning depth** | Reasoning, math, multi-step | Models with adaptive thinking: raise `effort`. Models without it: "Think step by step..." |
|
|
107
107
|
| **Role Prompting** | Expertise, specific perspective | "You are an expert tax accountant..." |
|
|
108
108
|
| **Structured Output** | Need parseable JSON/XML | Include schema + format enforcement |
|
|
@@ -164,7 +164,7 @@ The 7 apples cost $11.20
|
|
|
164
164
|
| Reasoning chain | "Explain your reasoning" | Justification needed |
|
|
165
165
|
|
|
166
166
|
**Zero-shot CoT:**
|
|
167
|
-
|
|
167
|
+
Append "Let's think step by step." only on models without built-in thinking. On models with adaptive thinking, control depth with `effort`; asking for the reasoning in the output can be declined as reasoning extraction.
|
|
168
168
|
|
|
169
169
|
---
|
|
170
170
|
|
|
@@ -55,7 +55,7 @@ class AgentConfig:
|
|
|
55
55
|
max_iterations: int = 10
|
|
56
56
|
system_prompt: str = ""
|
|
57
57
|
temperature: float = 0.7
|
|
58
|
-
model: str = "
|
|
58
|
+
model: str = ""
|
|
59
59
|
|
|
60
60
|
|
|
61
61
|
@dataclass
|
|
@@ -177,7 +177,7 @@ def load_config(path: Path) -> AgentConfig:
|
|
|
177
177
|
max_iterations=int(data.get('max_iterations', 10)),
|
|
178
178
|
system_prompt=data.get('system_prompt', ''),
|
|
179
179
|
temperature=float(data.get('temperature', 0.7)),
|
|
180
|
-
model=data.get('model', '
|
|
180
|
+
model=data.get('model', '')
|
|
181
181
|
)
|
|
182
182
|
|
|
183
183
|
|
|
@@ -369,44 +369,39 @@ def generate_mermaid_diagram(config: AgentConfig) -> str:
|
|
|
369
369
|
return '\n'.join(lines)
|
|
370
370
|
|
|
371
371
|
|
|
372
|
-
def estimate_cost(config: AgentConfig, runs: int = 100
|
|
373
|
-
|
|
372
|
+
def estimate_cost(config: AgentConfig, runs: int = 100,
|
|
373
|
+
price_in: Optional[float] = None, price_out: Optional[float] = None) -> Dict[str, Any]:
|
|
374
|
+
"""Estimate tokens per run; dollars only when the caller supplies current prices.
|
|
375
|
+
|
|
376
|
+
Prices are USD per million tokens. They are not built in because provider prices
|
|
377
|
+
change and a stale table gives confidently wrong numbers.
|
|
378
|
+
"""
|
|
374
379
|
validation = validate_agent(config)
|
|
375
380
|
min_tokens, max_tokens = validation.estimated_tokens_per_run
|
|
376
381
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
'claude-3-opus': {'input': 0.015, 'output': 0.075},
|
|
383
|
-
'claude-3-sonnet': {'input': 0.003, 'output': 0.015},
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
model_cost = costs.get(config.model, costs['gpt-4'])
|
|
387
|
-
|
|
388
|
-
# Assume 60% input, 40% output
|
|
389
|
-
input_tokens = min_tokens * 0.6
|
|
390
|
-
output_tokens = min_tokens * 0.4
|
|
382
|
+
def dollars(tokens: float) -> Optional[float]:
|
|
383
|
+
if price_in is None or price_out is None:
|
|
384
|
+
return None
|
|
385
|
+
# Assume 60% input, 40% output
|
|
386
|
+
return (tokens * 0.6 * price_in + tokens * 0.4 * price_out) / 1_000_000
|
|
391
387
|
|
|
392
|
-
|
|
393
|
-
|
|
388
|
+
per_run_min, per_run_max = dollars(min_tokens), dollars(max_tokens)
|
|
389
|
+
monthly_runs = runs * 30
|
|
394
390
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
cost_per_run_max = (input_tokens_max / 1000 * model_cost['input'] +
|
|
398
|
-
output_tokens_max / 1000 * model_cost['output'])
|
|
399
|
-
|
|
400
|
-
return {
|
|
401
|
-
'model': config.model,
|
|
391
|
+
result = {
|
|
392
|
+
'model': config.model or None,
|
|
402
393
|
'tokens_per_run': {'min': min_tokens, 'max': max_tokens},
|
|
403
|
-
'cost_per_run': {'min': round(cost_per_run_min, 4), 'max': round(cost_per_run_max, 4)},
|
|
404
394
|
'estimated_monthly': {
|
|
405
|
-
'runs':
|
|
406
|
-
'
|
|
407
|
-
'
|
|
408
|
-
}
|
|
395
|
+
'runs': monthly_runs,
|
|
396
|
+
'tokens_min': min_tokens * monthly_runs,
|
|
397
|
+
'tokens_max': max_tokens * monthly_runs,
|
|
398
|
+
},
|
|
409
399
|
}
|
|
400
|
+
if per_run_min is not None:
|
|
401
|
+
result['cost_per_run'] = {'min': round(per_run_min, 4), 'max': round(per_run_max, 4)}
|
|
402
|
+
result['estimated_monthly']['cost_min'] = round(per_run_min * monthly_runs, 2)
|
|
403
|
+
result['estimated_monthly']['cost_max'] = round(per_run_max * monthly_runs, 2)
|
|
404
|
+
return result
|
|
410
405
|
|
|
411
406
|
|
|
412
407
|
def format_validation_report(config: AgentConfig, result: ValidationResult) -> str:
|
|
@@ -461,6 +456,9 @@ def format_validation_report(config: AgentConfig, result: ValidationResult) -> s
|
|
|
461
456
|
|
|
462
457
|
|
|
463
458
|
def main():
|
|
459
|
+
# The report uses emoji; a Windows console defaults to cp1252 and cannot print them.
|
|
460
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
461
|
+
sys.stdout.reconfigure(encoding="utf-8")
|
|
464
462
|
parser = argparse.ArgumentParser(
|
|
465
463
|
description="Agent Orchestrator - Design and validate agent workflows",
|
|
466
464
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
@@ -469,13 +467,13 @@ Examples:
|
|
|
469
467
|
%(prog)s agent.yaml --validate
|
|
470
468
|
%(prog)s agent.yaml --visualize
|
|
471
469
|
%(prog)s agent.yaml --visualize --format mermaid
|
|
472
|
-
%(prog)s agent.yaml --estimate-cost --runs 100
|
|
470
|
+
%(prog)s agent.yaml --estimate-cost --runs 100 --price-in 4 --price-out 20
|
|
473
471
|
|
|
474
472
|
Agent config format (YAML):
|
|
475
473
|
|
|
476
474
|
name: research_assistant
|
|
477
475
|
pattern: react
|
|
478
|
-
model:
|
|
476
|
+
model: claude-opus-5-5
|
|
479
477
|
max_iterations: 10
|
|
480
478
|
tools:
|
|
481
479
|
- name: web_search
|
|
@@ -493,6 +491,8 @@ tools:
|
|
|
493
491
|
help='Visualization format (default: ascii)')
|
|
494
492
|
parser.add_argument('--estimate-cost', '-e', action='store_true', help='Estimate token costs')
|
|
495
493
|
parser.add_argument('--runs', '-r', type=int, default=100, help='Daily runs for cost estimation')
|
|
494
|
+
parser.add_argument('--price-in', type=float, help='Current input price, USD per million tokens')
|
|
495
|
+
parser.add_argument('--price-out', type=float, help='Current output price, USD per million tokens')
|
|
496
496
|
parser.add_argument('--output', '-o', help='Output file path')
|
|
497
497
|
parser.add_argument('--json', '-j', action='store_true', help='Output as JSON')
|
|
498
498
|
|
|
@@ -534,18 +534,21 @@ tools:
|
|
|
534
534
|
|
|
535
535
|
# Cost estimation
|
|
536
536
|
if args.estimate_cost:
|
|
537
|
-
costs = estimate_cost(config, args.runs)
|
|
537
|
+
costs = estimate_cost(config, args.runs, args.price_in, args.price_out)
|
|
538
538
|
if args.json:
|
|
539
539
|
output_parts.append(json.dumps(costs, indent=2))
|
|
540
540
|
else:
|
|
541
541
|
output_parts.append("")
|
|
542
542
|
output_parts.append("💰 COST ESTIMATION")
|
|
543
|
-
output_parts.append(f" Model: {costs['model']}")
|
|
543
|
+
output_parts.append(f" Model: {costs['model'] or '(not set)'}")
|
|
544
544
|
output_parts.append(f" Tokens per run: {costs['tokens_per_run']['min']:,} - {costs['tokens_per_run']['max']:,}")
|
|
545
|
-
|
|
546
|
-
output_parts.append(f" Monthly ({
|
|
547
|
-
|
|
548
|
-
|
|
545
|
+
monthly = costs['estimated_monthly']
|
|
546
|
+
output_parts.append(f" Monthly ({monthly['runs']:,} runs): {monthly['tokens_min']:,} - {monthly['tokens_max']:,} tokens")
|
|
547
|
+
if 'cost_per_run' in costs:
|
|
548
|
+
output_parts.append(f" Cost per run: ${costs['cost_per_run']['min']:.4f} - ${costs['cost_per_run']['max']:.4f}")
|
|
549
|
+
output_parts.append(f" Monthly cost: ${monthly['cost_min']:.2f} - ${monthly['cost_max']:.2f}")
|
|
550
|
+
else:
|
|
551
|
+
output_parts.append(" Cost: pass --price-in and --price-out (USD per million tokens) to get dollars")
|
|
549
552
|
|
|
550
553
|
# Output
|
|
551
554
|
output = '\n'.join(output_parts)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Prompt Optimizer - Static analysis tool for prompt engineering
|
|
4
4
|
|
|
5
5
|
Features:
|
|
6
|
-
-
|
|
6
|
+
- Approximate token count (characters / 4, not a real tokenizer)
|
|
7
7
|
- Prompt structure analysis
|
|
8
8
|
- Clarity scoring
|
|
9
9
|
- Few-shot example extraction and management
|
|
@@ -11,7 +11,7 @@ Features:
|
|
|
11
11
|
|
|
12
12
|
Usage:
|
|
13
13
|
python prompt_optimizer.py prompt.txt --analyze
|
|
14
|
-
python prompt_optimizer.py prompt.txt --tokens
|
|
14
|
+
python prompt_optimizer.py prompt.txt --tokens
|
|
15
15
|
python prompt_optimizer.py prompt.txt --optimize --output optimized.txt
|
|
16
16
|
python prompt_optimizer.py prompt.txt --extract-examples --output examples.json
|
|
17
17
|
"""
|
|
@@ -25,32 +25,15 @@ from typing import Dict, List, Optional, Tuple
|
|
|
25
25
|
from dataclasses import dataclass, asdict
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
#
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
'gpt-3.5': 4.0,
|
|
32
|
-
'claude': 3.5,
|
|
33
|
-
'default': 4.0
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
# Cost per 1K tokens (input)
|
|
37
|
-
COST_PER_1K = {
|
|
38
|
-
'gpt-4': 0.03,
|
|
39
|
-
'gpt-4-turbo': 0.01,
|
|
40
|
-
'gpt-3.5-turbo': 0.0005,
|
|
41
|
-
'claude-3-opus': 0.015,
|
|
42
|
-
'claude-3-sonnet': 0.003,
|
|
43
|
-
'claude-3-haiku': 0.00025,
|
|
44
|
-
'default': 0.01
|
|
45
|
-
}
|
|
28
|
+
# Rough characters-per-token ratio. Real tokenizers differ by model and by text, so
|
|
29
|
+
# every count here is an estimate; use the provider's token-counting API for exact numbers.
|
|
30
|
+
CHARS_PER_TOKEN = 4.0
|
|
46
31
|
|
|
47
32
|
|
|
48
33
|
@dataclass
|
|
49
34
|
class PromptAnalysis:
|
|
50
35
|
"""Results of prompt analysis"""
|
|
51
36
|
token_count: int
|
|
52
|
-
estimated_cost: float
|
|
53
|
-
model: str
|
|
54
37
|
clarity_score: int
|
|
55
38
|
structure_score: int
|
|
56
39
|
issues: List[Dict[str, str]]
|
|
@@ -71,16 +54,9 @@ class FewShotExample:
|
|
|
71
54
|
index: int
|
|
72
55
|
|
|
73
56
|
|
|
74
|
-
def estimate_tokens(text: str
|
|
75
|
-
"""
|
|
76
|
-
|
|
77
|
-
return int(len(text) / ratio)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def estimate_cost(token_count: int, model: str = 'default') -> float:
|
|
81
|
-
"""Estimate cost based on token count"""
|
|
82
|
-
cost_per_1k = COST_PER_1K.get(model, COST_PER_1K['default'])
|
|
83
|
-
return round((token_count / 1000) * cost_per_1k, 6)
|
|
57
|
+
def estimate_tokens(text: str) -> int:
|
|
58
|
+
"""Approximate token count from the character count"""
|
|
59
|
+
return int(len(text) / CHARS_PER_TOKEN)
|
|
84
60
|
|
|
85
61
|
|
|
86
62
|
def find_ambiguous_instructions(text: str) -> List[Dict[str, str]]:
|
|
@@ -89,11 +65,9 @@ def find_ambiguous_instructions(text: str) -> List[Dict[str, str]]:
|
|
|
89
65
|
|
|
90
66
|
# Vague verbs that need specificity
|
|
91
67
|
vague_patterns = [
|
|
92
|
-
(r'\b(analyze|process|handle|deal with)\b', 'Vague verb - specify the exact action'),
|
|
93
68
|
(r'\b(good|nice|appropriate|suitable)\b', 'Subjective term - define specific criteria'),
|
|
94
69
|
(r'\b(etc\.|and so on|and more)\b', 'Open-ended list - enumerate all items explicitly'),
|
|
95
70
|
(r'\b(if needed|as necessary|when appropriate)\b', 'Conditional without criteria - specify when'),
|
|
96
|
-
(r'\b(some|several|many|few|various)\b', 'Vague quantity - use specific numbers'),
|
|
97
71
|
]
|
|
98
72
|
|
|
99
73
|
lines = text.split('\n')
|
|
@@ -155,7 +129,7 @@ def check_output_format(text: str) -> Tuple[bool, List[str]]:
|
|
|
155
129
|
has_format = any(re.search(p, text, re.IGNORECASE) for p in format_indicators)
|
|
156
130
|
|
|
157
131
|
if not has_format:
|
|
158
|
-
suggestions.append('
|
|
132
|
+
suggestions.append('State the expected output format (structure, length, what to include)')
|
|
159
133
|
|
|
160
134
|
return has_format, suggestions
|
|
161
135
|
|
|
@@ -277,7 +251,7 @@ def generate_suggestions(analysis: PromptAnalysis) -> List[str]:
|
|
|
277
251
|
suggestions = []
|
|
278
252
|
|
|
279
253
|
if not analysis.has_output_format:
|
|
280
|
-
suggestions.append('
|
|
254
|
+
suggestions.append('State the output format. If code will parse the response as JSON, enforce it with structured outputs instead of describing it in the prompt')
|
|
281
255
|
|
|
282
256
|
if analysis.example_count == 0:
|
|
283
257
|
suggestions.append('Consider adding 2-3 few-shot examples for consistent outputs')
|
|
@@ -289,22 +263,14 @@ def generate_suggestions(analysis: PromptAnalysis) -> List[str]:
|
|
|
289
263
|
if analysis.clarity_score < 70:
|
|
290
264
|
suggestions.append('Improve clarity: replace vague terms with specific instructions')
|
|
291
265
|
|
|
292
|
-
if analysis.token_count > 2000:
|
|
293
|
-
suggestions.append(f'Prompt is {analysis.token_count} tokens - consider condensing for cost efficiency')
|
|
294
|
-
|
|
295
|
-
# Check for role prompting
|
|
296
|
-
if not re.search(r'you are|act as|as a\s+\w+', analysis.sections[0].get('content', [''])[0] if analysis.sections else '', re.IGNORECASE):
|
|
297
|
-
suggestions.append('Consider adding role context: "You are an expert..."')
|
|
298
|
-
|
|
299
266
|
return suggestions
|
|
300
267
|
|
|
301
268
|
|
|
302
|
-
def analyze_prompt(text: str
|
|
269
|
+
def analyze_prompt(text: str) -> PromptAnalysis:
|
|
303
270
|
"""Perform comprehensive prompt analysis"""
|
|
304
271
|
|
|
305
272
|
# Basic metrics
|
|
306
|
-
token_count = estimate_tokens(text
|
|
307
|
-
cost = estimate_cost(token_count, model)
|
|
273
|
+
token_count = estimate_tokens(text)
|
|
308
274
|
word_count = len(text.split())
|
|
309
275
|
line_count = len(text.split('\n'))
|
|
310
276
|
|
|
@@ -324,8 +290,6 @@ def analyze_prompt(text: str, model: str = 'gpt-4') -> PromptAnalysis:
|
|
|
324
290
|
|
|
325
291
|
analysis = PromptAnalysis(
|
|
326
292
|
token_count=token_count,
|
|
327
|
-
estimated_cost=cost,
|
|
328
|
-
model=model,
|
|
329
293
|
clarity_score=clarity_score,
|
|
330
294
|
structure_score=structure_score,
|
|
331
295
|
issues=all_issues,
|
|
@@ -367,8 +331,7 @@ def format_report(analysis: PromptAnalysis) -> str:
|
|
|
367
331
|
report.append("")
|
|
368
332
|
|
|
369
333
|
report.append("📊 METRICS")
|
|
370
|
-
report.append(f"
|
|
371
|
-
report.append(f" Estimated cost: ${analysis.estimated_cost:.4f} ({analysis.model})")
|
|
334
|
+
report.append(f" Tokens (approx): {analysis.token_count:,}")
|
|
372
335
|
report.append(f" Word count: {analysis.word_count:,}")
|
|
373
336
|
report.append(f" Line count: {analysis.line_count}")
|
|
374
337
|
report.append("")
|
|
@@ -411,13 +374,16 @@ def format_report(analysis: PromptAnalysis) -> str:
|
|
|
411
374
|
|
|
412
375
|
|
|
413
376
|
def main():
|
|
377
|
+
# The report uses emoji; a Windows console defaults to cp1252 and cannot print them.
|
|
378
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
379
|
+
sys.stdout.reconfigure(encoding="utf-8")
|
|
414
380
|
parser = argparse.ArgumentParser(
|
|
415
381
|
description="Prompt Optimizer - Analyze and optimize prompts",
|
|
416
382
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
417
383
|
epilog="""
|
|
418
384
|
Examples:
|
|
419
385
|
%(prog)s prompt.txt --analyze
|
|
420
|
-
%(prog)s prompt.txt --tokens
|
|
386
|
+
%(prog)s prompt.txt --tokens
|
|
421
387
|
%(prog)s prompt.txt --optimize --output optimized.txt
|
|
422
388
|
%(prog)s prompt.txt --extract-examples --output examples.json
|
|
423
389
|
"""
|
|
@@ -428,9 +394,6 @@ Examples:
|
|
|
428
394
|
parser.add_argument('--tokens', '-t', action='store_true', help='Count tokens only')
|
|
429
395
|
parser.add_argument('--optimize', '-O', action='store_true', help='Generate optimized version')
|
|
430
396
|
parser.add_argument('--extract-examples', '-e', action='store_true', help='Extract few-shot examples')
|
|
431
|
-
parser.add_argument('--model', '-m', default='gpt-4',
|
|
432
|
-
choices=['gpt-4', 'gpt-4-turbo', 'gpt-3.5-turbo', 'claude-3-opus', 'claude-3-sonnet', 'claude-3-haiku'],
|
|
433
|
-
help='Model for token/cost estimation')
|
|
434
397
|
parser.add_argument('--output', '-o', help='Output file path')
|
|
435
398
|
parser.add_argument('--json', '-j', action='store_true', help='Output as JSON')
|
|
436
399
|
parser.add_argument('--compare', '-c', help='Compare with baseline analysis JSON')
|
|
@@ -447,17 +410,11 @@ Examples:
|
|
|
447
410
|
|
|
448
411
|
# Tokens only
|
|
449
412
|
if args.tokens:
|
|
450
|
-
token_count = estimate_tokens(text
|
|
451
|
-
cost = estimate_cost(token_count, args.model)
|
|
413
|
+
token_count = estimate_tokens(text)
|
|
452
414
|
if args.json:
|
|
453
|
-
print(json.dumps({
|
|
454
|
-
'tokens': token_count,
|
|
455
|
-
'cost': cost,
|
|
456
|
-
'model': args.model
|
|
457
|
-
}, indent=2))
|
|
415
|
+
print(json.dumps({'tokens_approx': token_count}, indent=2))
|
|
458
416
|
else:
|
|
459
|
-
print(f"Tokens: {token_count:,}")
|
|
460
|
-
print(f"Estimated cost: ${cost:.4f} ({args.model})")
|
|
417
|
+
print(f"Tokens (approx): {token_count:,}")
|
|
461
418
|
sys.exit(0)
|
|
462
419
|
|
|
463
420
|
# Extract examples
|
|
@@ -481,8 +438,8 @@ Examples:
|
|
|
481
438
|
print(f"Optimized prompt written to {args.output}")
|
|
482
439
|
|
|
483
440
|
# Show comparison
|
|
484
|
-
orig_tokens = estimate_tokens(text
|
|
485
|
-
new_tokens = estimate_tokens(optimized
|
|
441
|
+
orig_tokens = estimate_tokens(text)
|
|
442
|
+
new_tokens = estimate_tokens(optimized)
|
|
486
443
|
saved = orig_tokens - new_tokens
|
|
487
444
|
print(f"Tokens: {orig_tokens:,} -> {new_tokens:,} (saved {saved:,})")
|
|
488
445
|
else:
|
|
@@ -490,7 +447,7 @@ Examples:
|
|
|
490
447
|
sys.exit(0)
|
|
491
448
|
|
|
492
449
|
# Default: full analysis
|
|
493
|
-
analysis = analyze_prompt(text
|
|
450
|
+
analysis = analyze_prompt(text)
|
|
494
451
|
|
|
495
452
|
# Compare with baseline
|
|
496
453
|
if args.compare:
|
|
@@ -180,7 +180,7 @@ if (p.x < 1.0 || p.y < 1.0 ||
|
|
|
180
180
|
c.xyw *= 0.0;
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
//
|
|
183
|
+
// Ink decay: must use multiplicative decay; subtractive decay causes saturation in high-concentration areas and overly fast decay in low-concentration areas
|
|
184
184
|
c.w *= 0.995; // 0.5% decay per frame, adjustable [0.99=fast dissipation, 0.999=persistent]
|
|
185
185
|
|
|
186
186
|
// Numerical clamping (prevent explosion)
|
|
@@ -199,14 +199,14 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
|
199
199
|
vec2 uv = fragCoord / iResolution.xy;
|
|
200
200
|
vec4 c = texture(iChannel0, uv);
|
|
201
201
|
|
|
202
|
-
//
|
|
202
|
+
// Color base must be bright enough! 0.5+0.5*cos produces bright colors in [0,1] range
|
|
203
203
|
// Never use extremely dark base colors like vec3(0.02, 0.01, 0.08) — multiplied by ink, they become invisible
|
|
204
204
|
vec3 col = 0.5 + 0.5 * cos(atan(c.y, c.x) + vec3(0.0, 2.1, 4.2));
|
|
205
|
-
//
|
|
205
|
+
// Use smoothstep instead of linear division to preserve gradient variation
|
|
206
206
|
float ink = smoothstep(0.0, 2.0, c.w);
|
|
207
207
|
col *= ink;
|
|
208
208
|
|
|
209
|
-
//
|
|
209
|
+
// Background color must be visible to the eye (RGB at least > 5/255 ≈ 0.02), otherwise users think the page is all black
|
|
210
210
|
col = max(col, vec3(0.02, 0.012, 0.035));
|
|
211
211
|
|
|
212
212
|
fragColor = vec4(col, 1.0);
|
|
@@ -50,12 +50,12 @@ Numerical viscosity over-smooths small-scale vortices. Vorticity confinement com
|
|
|
50
50
|
|
|
51
51
|
**Why**: GPU shaders cannot simultaneously read and write the same buffer. The ping-pong strategy reads from one buffer (previous frame's data) and writes to the other each frame, then swaps on the next frame.
|
|
52
52
|
|
|
53
|
-
**
|
|
53
|
+
**Key Difference Between ShaderToy and WebGL2**: In ShaderToy, Buffer A/B are two independent passes with separate write targets, so `iChannel0=self, iChannel1=other` doesn't conflict. However, in WebGL2 there's only one shader program doing ping-pong, and the write target texture cannot be simultaneously read. The solution is **dual-channel encoding** (R=current height, G=previous frame height).
|
|
54
54
|
|
|
55
55
|
**Code** (WebGL2-safe version, reads only from iChannel0, with RGBA8-compatible encoding):
|
|
56
56
|
```glsl
|
|
57
|
-
//
|
|
58
|
-
//
|
|
57
|
+
// Only use iChannel0 (read currentBuf), write to nextBuf (must be different!)
|
|
58
|
+
// encode/decode ensure signed values aren't clipped on RGBA8 (no float textures/SwiftShader)
|
|
59
59
|
uniform int useFloatTex;
|
|
60
60
|
float decode(float v) { return useFloatTex == 1 ? v : v * 2.0 - 1.0; }
|
|
61
61
|
float encode(float v) { return useFloatTex == 1 ? v : v * 0.5 + 0.5; }
|
|
@@ -244,7 +244,7 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord)
|
|
|
244
244
|
#define SIZY 64.0 // adjustable: cloth height (particle count)
|
|
245
245
|
|
|
246
246
|
// Left half [0, SIZX) stores positions, right half [SIZX, 2*SIZX) stores velocities
|
|
247
|
-
//
|
|
247
|
+
// In WebGL2, getpos/getvel both read from iChannel0 (currentBuf, read-only),
|
|
248
248
|
// write target is nextBuf (separate buffer), avoiding read-write conflict
|
|
249
249
|
vec3 getpos(vec2 id)
|
|
250
250
|
{
|
|
@@ -298,7 +298,7 @@ void edge(vec2 dif)
|
|
|
298
298
|
vec3 posdif = getpos(dif + c) - pos;
|
|
299
299
|
vec3 veldif = getvel(dif + c) - ovel;
|
|
300
300
|
|
|
301
|
-
//
|
|
301
|
+
// Must check for zero length, otherwise normalize(vec3(0)) produces NaN
|
|
302
302
|
float plen = length(posdif);
|
|
303
303
|
if (plen < 0.0001) return;
|
|
304
304
|
vec3 dir = posdif / plen;
|
|
@@ -498,7 +498,7 @@ if (length(pos - ballPos) < ballRadius) {
|
|
|
498
498
|
}
|
|
499
499
|
```
|
|
500
500
|
|
|
501
|
-
> **
|
|
501
|
+
> **Common Pitfalls**:
|
|
502
502
|
> - **Cloth Image Pass must project world coordinates to screen**: You cannot use `uv * vec2(SIZX, SIZY)` to map screen UV to grid ID, because particles have moved from their initial positions, producing scattered fragments. You must iterate over mesh faces, projecting vertex world coordinates to screen space for triangle rasterization
|
|
503
503
|
> - GLSL is strictly typed; you cannot write `float / vec2`. Wrong example: `length(dif) / vec2(SIZX, SIZY).x` will first execute float/vec2 causing a compile error; use `length(dif) / SIZX` instead
|
|
504
504
|
> - `normalize(vec3(0))` produces NaN; all `normalize()` calls must include a length check beforehand
|
|
@@ -619,7 +619,7 @@ A complete voxel water scene with surface wave reflections, underwater refractio
|
|
|
619
619
|
float waterY = 0.0;
|
|
620
620
|
|
|
621
621
|
// Underwater voxel scene (sand + seaweed)
|
|
622
|
-
//
|
|
622
|
+
// c.xz returns vec2, which only has .x/.y components — never use .z!
|
|
623
623
|
int getVoxel(vec3 c) {
|
|
624
624
|
float sandHeight = -3.0 + 0.5 * sin(c.x * 0.3) * cos(c.z * 0.4);
|
|
625
625
|
if (c.y < sandHeight) return 1; // Sand interior
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
## WebGL2 Adaptation Requirements
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**GLSL Type Strictness**: float and vec types cannot be implicitly converted. `vec3 v = 1.0;` is illegal; you must use the vector form (e.g., `vec3(1.0)`, `vec3(1.0) * x`, `value * vec3(1.0)`).
|
|
4
4
|
|
|
5
5
|
The code templates in this document use ShaderToy GLSL style. When generating standalone HTML pages, you must adapt for WebGL2:
|
|
6
6
|
|
|
@@ -193,7 +193,7 @@ vec3 shade(vec3 pos, vec3 normal, vec3 rd, vec3 albedo) {
|
|
|
193
193
|
}
|
|
194
194
|
```
|
|
195
195
|
|
|
196
|
-
> **
|
|
196
|
+
> **Critical pitfall**: `d.xy` must be passed as distBound, and `d.y` must be updated each time a closer intersection is found! If the deployed code passes the original `dist` directly without updating, the intersection logic will fail (all object distance tests become invalid), resulting in a completely black screen.
|
|
197
197
|
|
|
198
198
|
```glsl
|
|
199
199
|
#define MAX_BOUNCES 4
|
|
@@ -229,7 +229,7 @@ vec3 radiance(vec3 ro, vec3 rd) {
|
|
|
229
229
|
|
|
230
230
|
Runs directly on ShaderToy, includes sphere, plane, and box primitives with reflection and Blinn-Phong shading.
|
|
231
231
|
|
|
232
|
-
> **
|
|
232
|
+
> **Must follow**: All intersection function calls must use `d.xy` as the `distBound` parameter, and update `d.y` after each closer intersection is found. Incorrect usage: `iSphere(ro, rd, dist, ...)` (always using the original dist). Correct usage: `iSphere(ro, rd, d.xy, ...)` followed by `if (t < d.y) { d.y = t; ... }` to update.
|
|
233
233
|
|
|
234
234
|
```glsl
|
|
235
235
|
// Analytic Ray Tracing - Complete ShaderToy Template
|
|
@@ -117,8 +117,8 @@ float fbm(vec2 p) {
|
|
|
117
117
|
|
|
118
118
|
```glsl
|
|
119
119
|
// Map scalar field (0~1) to color using a mix chain
|
|
120
|
-
//
|
|
121
|
-
//
|
|
120
|
+
// GLSL is strictly typed. Variable declarations must be complete, e.g. vec3 col = vec3(0.2, 0.1, 0.4)
|
|
121
|
+
// Decimals must be written as 0.x, not .x (division by zero errors)
|
|
122
122
|
vec3 palette(float t) {
|
|
123
123
|
vec3 col = vec3(0.2, 0.1, 0.4); // deep purple base
|
|
124
124
|
col = mix(col, vec3(0.3, 0.05, 0.05), t); // dark red
|