@r1z4x/tezgah 1.0.0
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 +738 -0
- package/LICENSE +21 -0
- package/MANIFEST +753 -0
- package/README.de.md +169 -0
- package/README.es.md +168 -0
- package/README.fr.md +166 -0
- package/README.md +161 -0
- package/README.tr.md +163 -0
- package/agents/tezgah-reviewer.md +70 -0
- package/bin/codegen +337 -0
- package/bin/consult +797 -0
- package/bin/tezgah-adhd +50 -0
- package/bin/tezgah-agents +45 -0
- package/bin/tezgah-capture +66 -0
- package/bin/tezgah-context +96 -0
- package/bin/tezgah-decisions +213 -0
- package/bin/tezgah-design +953 -0
- package/bin/tezgah-docs +1199 -0
- package/bin/tezgah-doctor +497 -0
- package/bin/tezgah-dsh +37 -0
- package/bin/tezgah-dsh.cmd +70 -0
- package/bin/tezgah-gate +75 -0
- package/bin/tezgah-import-ai-research +439 -0
- package/bin/tezgah-index +32 -0
- package/bin/tezgah-lessons +148 -0
- package/bin/tezgah-mcp +322 -0
- package/bin/tezgah-migrate +324 -0
- package/bin/tezgah-pony +50 -0
- package/bin/tezgah-research +772 -0
- package/bin/tezgah-rollback +93 -0
- package/bin/tezgah-route +124 -0
- package/bin/tezgah-setup +5798 -0
- package/bin/tezgah-status +383 -0
- package/bin/tezgah-task +607 -0
- package/bin/tezgah-taste +423 -0
- package/bin/tezgah-triage +485 -0
- package/bin/tezgah.js +36 -0
- package/commands/adhd.md +21 -0
- package/commands/ponytail.md +21 -0
- package/docs/README.md +112 -0
- package/docs/architecture.md +226 -0
- package/docs/citations-baseline.json +59 -0
- package/docs/clarity-baseline.json +116 -0
- package/docs/contract.md +416 -0
- package/docs/environment.md +141 -0
- package/docs/evidence.md +831 -0
- package/docs/feature-audit.md +139 -0
- package/docs/gate.md +407 -0
- package/docs/glossary.md +201 -0
- package/docs/hosts.md +400 -0
- package/docs/index.json +471 -0
- package/docs/judge.md +193 -0
- package/docs/layers.md +189 -0
- package/docs/models.md +160 -0
- package/docs/operations.md +666 -0
- package/docs/research.md +809 -0
- package/docs/results/paired-outcome-2026-10.jsonl +254 -0
- package/docs/results/paired-outcome-2026-10.md +204 -0
- package/docs/skills.md +236 -0
- package/docs/status-line.md +315 -0
- package/docs/testing.md +287 -0
- package/docs/vision.md +117 -0
- package/hooks/hooks.json +33 -0
- package/hooks/projects-auto-init.py +98 -0
- package/hooks/projects-posttooluse.py +149 -0
- package/hooks/projects-pretooluse.py +40 -0
- package/hooks/projects-stop.py +60 -0
- package/hooks/tezgah_agents.py +817 -0
- package/hooks/tezgah_apps.py +266 -0
- package/hooks/tezgah_attest.py +255 -0
- package/hooks/tezgah_clarity.py +171 -0
- package/hooks/tezgah_components.py +140 -0
- package/hooks/tezgah_context.py +2953 -0
- package/hooks/tezgah_embed.py +765 -0
- package/hooks/tezgah_gate.py +2736 -0
- package/hooks/tezgah_guard.py +177 -0
- package/hooks/tezgah_index.py +124 -0
- package/hooks/tezgah_integrity.py +5141 -0
- package/hooks/tezgah_judge.py +487 -0
- package/hooks/tezgah_lang.py +230 -0
- package/hooks/tezgah_lessons.py +210 -0
- package/hooks/tezgah_models.py +855 -0
- package/hooks/tezgah_paths.py +1132 -0
- package/hooks/tezgah_policy.py +1039 -0
- package/hooks/tezgah_rank.py +77 -0
- package/hooks/tezgah_replay.py +1109 -0
- package/hooks/tezgah_research.py +5176 -0
- package/hooks/tezgah_shapes.py +305 -0
- package/hooks/tezgah_skill_pick.py +227 -0
- package/hooks/tezgah_snapshot.py +490 -0
- package/hooks/tezgah_stopspec.py +320 -0
- package/hooks/tezgah_task.py +458 -0
- package/hooks/tezgah_taste.py +201 -0
- package/hooks/tezgah_untrusted.py +112 -0
- package/hooks/tezgah_update.py +430 -0
- package/hosts/codex/hook.py +268 -0
- package/hosts/codex/hooks.json +26 -0
- package/hosts/cursor/hook.py +461 -0
- package/hosts/cursor/hooks.json +44 -0
- package/hosts/dsh/hooks.json +21 -0
- package/hosts/dsh/statusline/lib/client.js +138 -0
- package/hosts/dsh/statusline/lib/index.js +104 -0
- package/hosts/dsh/statusline/package.json +28 -0
- package/hosts/omp/hook.py +277 -0
- package/hosts/omp/tezgah-hook.ts.in +701 -0
- package/hosts/opencode/plugins/tezgah.js +2742 -0
- package/hosts/opencode/tui/tezgah-tui.tsx +151 -0
- package/output-styles/tezgah.md +142 -0
- package/package.json +32 -0
- package/packaging/build.sh +342 -0
- package/packaging/install.ps1 +166 -0
- package/packaging/install.sh +56 -0
- package/packaging/upgrade.sh +142 -0
- package/skills/ai-research/0-autoresearch-skill/SKILL.md +411 -0
- package/skills/ai-research/0-autoresearch-skill/references/agent-continuity.md +95 -0
- package/skills/ai-research/0-autoresearch-skill/references/progress-reporting.md +166 -0
- package/skills/ai-research/0-autoresearch-skill/references/skill-routing.md +219 -0
- package/skills/ai-research/0-autoresearch-skill/templates/findings.md +44 -0
- package/skills/ai-research/0-autoresearch-skill/templates/research-log.md +41 -0
- package/skills/ai-research/01-model-architecture/litgpt/SKILL.md +469 -0
- package/skills/ai-research/01-model-architecture/litgpt/references/custom-models.md +569 -0
- package/skills/ai-research/01-model-architecture/litgpt/references/distributed-training.md +452 -0
- package/skills/ai-research/01-model-architecture/litgpt/references/supported-models.md +337 -0
- package/skills/ai-research/01-model-architecture/litgpt/references/training-recipes.md +620 -0
- package/skills/ai-research/01-model-architecture/mamba/SKILL.md +260 -0
- package/skills/ai-research/01-model-architecture/mamba/references/architecture-details.md +207 -0
- package/skills/ai-research/01-model-architecture/mamba/references/benchmarks.md +256 -0
- package/skills/ai-research/01-model-architecture/mamba/references/training-guide.md +389 -0
- package/skills/ai-research/01-model-architecture/nanogpt/SKILL.md +290 -0
- package/skills/ai-research/01-model-architecture/nanogpt/references/architecture.md +383 -0
- package/skills/ai-research/01-model-architecture/nanogpt/references/data.md +477 -0
- package/skills/ai-research/01-model-architecture/nanogpt/references/training.md +565 -0
- package/skills/ai-research/01-model-architecture/rwkv/SKILL.md +260 -0
- package/skills/ai-research/01-model-architecture/rwkv/references/architecture-details.md +345 -0
- package/skills/ai-research/01-model-architecture/rwkv/references/rwkv7.md +387 -0
- package/skills/ai-research/01-model-architecture/rwkv/references/state-management.md +370 -0
- package/skills/ai-research/01-model-architecture/torchtitan/SKILL.md +358 -0
- package/skills/ai-research/01-model-architecture/torchtitan/references/checkpoint.md +182 -0
- package/skills/ai-research/01-model-architecture/torchtitan/references/custom-models.md +259 -0
- package/skills/ai-research/01-model-architecture/torchtitan/references/float8.md +134 -0
- package/skills/ai-research/01-model-architecture/torchtitan/references/fsdp.md +127 -0
- package/skills/ai-research/02-tokenization/huggingface-tokenizers/SKILL.md +516 -0
- package/skills/ai-research/02-tokenization/huggingface-tokenizers/references/algorithms.md +654 -0
- package/skills/ai-research/02-tokenization/huggingface-tokenizers/references/integration.md +638 -0
- package/skills/ai-research/02-tokenization/huggingface-tokenizers/references/pipeline.md +724 -0
- package/skills/ai-research/02-tokenization/huggingface-tokenizers/references/training.md +566 -0
- package/skills/ai-research/02-tokenization/sentencepiece/SKILL.md +235 -0
- package/skills/ai-research/02-tokenization/sentencepiece/references/algorithms.md +201 -0
- package/skills/ai-research/02-tokenization/sentencepiece/references/training.md +305 -0
- package/skills/ai-research/03-fine-tuning/axolotl/SKILL.md +158 -0
- package/skills/ai-research/03-fine-tuning/axolotl/references/api.md +5549 -0
- package/skills/ai-research/03-fine-tuning/axolotl/references/dataset-formats.md +1030 -0
- package/skills/ai-research/03-fine-tuning/axolotl/references/index.md +16 -0
- package/skills/ai-research/03-fine-tuning/axolotl/references/other.md +3564 -0
- package/skills/ai-research/03-fine-tuning/llama-factory/SKILL.md +80 -0
- package/skills/ai-research/03-fine-tuning/llama-factory/references/_images.md +24 -0
- package/skills/ai-research/03-fine-tuning/llama-factory/references/advanced.md +1056 -0
- package/skills/ai-research/03-fine-tuning/llama-factory/references/getting_started.md +350 -0
- package/skills/ai-research/03-fine-tuning/llama-factory/references/index.md +20 -0
- package/skills/ai-research/03-fine-tuning/llama-factory/references/other.md +32 -0
- package/skills/ai-research/03-fine-tuning/peft/SKILL.md +431 -0
- package/skills/ai-research/03-fine-tuning/peft/references/advanced-usage.md +515 -0
- package/skills/ai-research/03-fine-tuning/peft/references/troubleshooting.md +481 -0
- package/skills/ai-research/03-fine-tuning/unsloth/SKILL.md +80 -0
- package/skills/ai-research/03-fine-tuning/unsloth/references/index.md +8 -0
- package/skills/ai-research/03-fine-tuning/unsloth/references/llms.md +83 -0
- package/skills/ai-research/04-mechanistic-interpretability/nnsight/SKILL.md +436 -0
- package/skills/ai-research/04-mechanistic-interpretability/nnsight/references/README.md +79 -0
- package/skills/ai-research/04-mechanistic-interpretability/nnsight/references/api.md +345 -0
- package/skills/ai-research/04-mechanistic-interpretability/nnsight/references/tutorials.md +301 -0
- package/skills/ai-research/04-mechanistic-interpretability/pyvene/SKILL.md +473 -0
- package/skills/ai-research/04-mechanistic-interpretability/pyvene/references/README.md +74 -0
- package/skills/ai-research/04-mechanistic-interpretability/pyvene/references/api.md +384 -0
- package/skills/ai-research/04-mechanistic-interpretability/pyvene/references/tutorials.md +377 -0
- package/skills/ai-research/04-mechanistic-interpretability/saelens/SKILL.md +386 -0
- package/skills/ai-research/04-mechanistic-interpretability/saelens/references/README.md +71 -0
- package/skills/ai-research/04-mechanistic-interpretability/saelens/references/api.md +334 -0
- package/skills/ai-research/04-mechanistic-interpretability/saelens/references/tutorials.md +319 -0
- package/skills/ai-research/04-mechanistic-interpretability/transformer-lens/SKILL.md +346 -0
- package/skills/ai-research/04-mechanistic-interpretability/transformer-lens/references/README.md +55 -0
- package/skills/ai-research/04-mechanistic-interpretability/transformer-lens/references/api.md +363 -0
- package/skills/ai-research/04-mechanistic-interpretability/transformer-lens/references/tutorials.md +340 -0
- package/skills/ai-research/05-data-processing/nemo-curator/SKILL.md +383 -0
- package/skills/ai-research/05-data-processing/nemo-curator/references/deduplication.md +88 -0
- package/skills/ai-research/05-data-processing/nemo-curator/references/filtering.md +103 -0
- package/skills/ai-research/05-data-processing/ray-data/SKILL.md +326 -0
- package/skills/ai-research/05-data-processing/ray-data/references/integration.md +83 -0
- package/skills/ai-research/05-data-processing/ray-data/references/transformations.md +84 -0
- package/skills/ai-research/06-post-training/grpo-rl-training/README.md +98 -0
- package/skills/ai-research/06-post-training/grpo-rl-training/SKILL.md +572 -0
- package/skills/ai-research/06-post-training/miles/SKILL.md +315 -0
- package/skills/ai-research/06-post-training/miles/references/api-reference.md +142 -0
- package/skills/ai-research/06-post-training/miles/references/troubleshooting.md +353 -0
- package/skills/ai-research/06-post-training/openrlhf/SKILL.md +249 -0
- package/skills/ai-research/06-post-training/openrlhf/references/algorithm-comparison.md +405 -0
- package/skills/ai-research/06-post-training/openrlhf/references/custom-rewards.md +531 -0
- package/skills/ai-research/06-post-training/openrlhf/references/hybrid-engine.md +288 -0
- package/skills/ai-research/06-post-training/openrlhf/references/multi-node-training.md +455 -0
- package/skills/ai-research/06-post-training/simpo/SKILL.md +219 -0
- package/skills/ai-research/06-post-training/simpo/references/datasets.md +479 -0
- package/skills/ai-research/06-post-training/simpo/references/hyperparameters.md +453 -0
- package/skills/ai-research/06-post-training/simpo/references/loss-functions.md +351 -0
- package/skills/ai-research/06-post-training/slime/SKILL.md +464 -0
- package/skills/ai-research/06-post-training/slime/references/api-reference.md +393 -0
- package/skills/ai-research/06-post-training/slime/references/troubleshooting.md +387 -0
- package/skills/ai-research/06-post-training/torchforge/SKILL.md +433 -0
- package/skills/ai-research/06-post-training/torchforge/references/api-reference.md +328 -0
- package/skills/ai-research/06-post-training/torchforge/references/troubleshooting.md +410 -0
- package/skills/ai-research/06-post-training/trl-fine-tuning/SKILL.md +455 -0
- package/skills/ai-research/06-post-training/trl-fine-tuning/references/dpo-variants.md +228 -0
- package/skills/ai-research/06-post-training/trl-fine-tuning/references/online-rl.md +83 -0
- package/skills/ai-research/06-post-training/trl-fine-tuning/references/reward-modeling.md +123 -0
- package/skills/ai-research/06-post-training/trl-fine-tuning/references/sft-training.md +169 -0
- package/skills/ai-research/06-post-training/verl/SKILL.md +391 -0
- package/skills/ai-research/06-post-training/verl/references/api-reference.md +302 -0
- package/skills/ai-research/06-post-training/verl/references/troubleshooting.md +392 -0
- package/skills/ai-research/07-safety-alignment/constitutional-ai/SKILL.md +290 -0
- package/skills/ai-research/07-safety-alignment/llamaguard/SKILL.md +337 -0
- package/skills/ai-research/07-safety-alignment/nemo-guardrails/SKILL.md +297 -0
- package/skills/ai-research/07-safety-alignment/prompt-guard/SKILL.md +312 -0
- package/skills/ai-research/08-distributed-training/accelerate/SKILL.md +332 -0
- package/skills/ai-research/08-distributed-training/accelerate/references/custom-plugins.md +454 -0
- package/skills/ai-research/08-distributed-training/accelerate/references/megatron-integration.md +490 -0
- package/skills/ai-research/08-distributed-training/accelerate/references/performance.md +526 -0
- package/skills/ai-research/08-distributed-training/deepspeed/SKILL.md +141 -0
- package/skills/ai-research/08-distributed-training/deepspeed/references/08.md +18 -0
- package/skills/ai-research/08-distributed-training/deepspeed/references/09.md +174 -0
- package/skills/ai-research/08-distributed-training/deepspeed/references/2020.md +379 -0
- package/skills/ai-research/08-distributed-training/deepspeed/references/2023.md +280 -0
- package/skills/ai-research/08-distributed-training/deepspeed/references/assets.md +180 -0
- package/skills/ai-research/08-distributed-training/deepspeed/references/index.md +36 -0
- package/skills/ai-research/08-distributed-training/deepspeed/references/mii.md +119 -0
- package/skills/ai-research/08-distributed-training/deepspeed/references/other.md +1192 -0
- package/skills/ai-research/08-distributed-training/megatron-core/SKILL.md +366 -0
- package/skills/ai-research/08-distributed-training/megatron-core/references/benchmarks.md +250 -0
- package/skills/ai-research/08-distributed-training/megatron-core/references/parallelism-guide.md +405 -0
- package/skills/ai-research/08-distributed-training/megatron-core/references/production-examples.md +474 -0
- package/skills/ai-research/08-distributed-training/megatron-core/references/training-recipes.md +548 -0
- package/skills/ai-research/08-distributed-training/pytorch-fsdp2/SKILL.md +231 -0
- package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/pytorch_dcp_async_recipe.md +14 -0
- package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/pytorch_dcp_overview.md +19 -0
- package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/pytorch_dcp_recipe.md +18 -0
- package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/pytorch_ddp_notes.md +10 -0
- package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/pytorch_device_mesh_tutorial.md +22 -0
- package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/pytorch_examples_fsdp2.md +15 -0
- package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/pytorch_fsdp1_api.md +9 -0
- package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/pytorch_fsdp2_tutorial.md +45 -0
- package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/pytorch_fully_shard_api.md +49 -0
- package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/pytorch_tp_tutorial.md +22 -0
- package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/ray_train_fsdp2_example.md +13 -0
- package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/torchtitan_fsdp_notes.md +14 -0
- package/skills/ai-research/08-distributed-training/pytorch-lightning/SKILL.md +346 -0
- package/skills/ai-research/08-distributed-training/pytorch-lightning/references/callbacks.md +437 -0
- package/skills/ai-research/08-distributed-training/pytorch-lightning/references/distributed.md +491 -0
- package/skills/ai-research/08-distributed-training/pytorch-lightning/references/hyperparameter-tuning.md +557 -0
- package/skills/ai-research/08-distributed-training/ray-train/SKILL.md +406 -0
- package/skills/ai-research/08-distributed-training/ray-train/references/multi-node.md +629 -0
- package/skills/ai-research/09-infrastructure/lambda-labs/SKILL.md +545 -0
- package/skills/ai-research/09-infrastructure/lambda-labs/references/advanced-usage.md +612 -0
- package/skills/ai-research/09-infrastructure/lambda-labs/references/troubleshooting.md +531 -0
- package/skills/ai-research/09-infrastructure/modal/SKILL.md +341 -0
- package/skills/ai-research/09-infrastructure/modal/references/advanced-usage.md +504 -0
- package/skills/ai-research/09-infrastructure/modal/references/troubleshooting.md +495 -0
- package/skills/ai-research/09-infrastructure/skypilot/SKILL.md +509 -0
- package/skills/ai-research/09-infrastructure/skypilot/references/advanced-usage.md +492 -0
- package/skills/ai-research/09-infrastructure/skypilot/references/troubleshooting.md +571 -0
- package/skills/ai-research/10-optimization/awq/SKILL.md +310 -0
- package/skills/ai-research/10-optimization/awq/references/advanced-usage.md +325 -0
- package/skills/ai-research/10-optimization/awq/references/troubleshooting.md +345 -0
- package/skills/ai-research/10-optimization/bitsandbytes/SKILL.md +411 -0
- package/skills/ai-research/10-optimization/bitsandbytes/references/memory-optimization.md +522 -0
- package/skills/ai-research/10-optimization/bitsandbytes/references/qlora-training.md +522 -0
- package/skills/ai-research/10-optimization/bitsandbytes/references/quantization-formats.md +448 -0
- package/skills/ai-research/10-optimization/flash-attention/SKILL.md +367 -0
- package/skills/ai-research/10-optimization/flash-attention/references/benchmarks.md +216 -0
- package/skills/ai-research/10-optimization/flash-attention/references/transformers-integration.md +294 -0
- package/skills/ai-research/10-optimization/gguf/SKILL.md +427 -0
- package/skills/ai-research/10-optimization/gguf/references/advanced-usage.md +505 -0
- package/skills/ai-research/10-optimization/gguf/references/troubleshooting.md +443 -0
- package/skills/ai-research/10-optimization/gptq/SKILL.md +450 -0
- package/skills/ai-research/10-optimization/gptq/references/calibration.md +338 -0
- package/skills/ai-research/10-optimization/gptq/references/integration.md +130 -0
- package/skills/ai-research/10-optimization/gptq/references/troubleshooting.md +96 -0
- package/skills/ai-research/10-optimization/hqq/SKILL.md +445 -0
- package/skills/ai-research/10-optimization/hqq/references/advanced-usage.md +529 -0
- package/skills/ai-research/10-optimization/hqq/references/troubleshooting.md +504 -0
- package/skills/ai-research/10-optimization/ml-training-recipes/SKILL.md +319 -0
- package/skills/ai-research/10-optimization/ml-training-recipes/references/architecture.md +320 -0
- package/skills/ai-research/10-optimization/ml-training-recipes/references/biomedical.md +498 -0
- package/skills/ai-research/10-optimization/ml-training-recipes/references/domain-specific.md +626 -0
- package/skills/ai-research/10-optimization/ml-training-recipes/references/experiment-loop.md +129 -0
- package/skills/ai-research/10-optimization/ml-training-recipes/references/optimizers.md +330 -0
- package/skills/ai-research/10-optimization/ml-training-recipes/references/scaling-and-selection.md +428 -0
- package/skills/ai-research/11-evaluation/bigcode-evaluation-harness/SKILL.md +405 -0
- package/skills/ai-research/11-evaluation/bigcode-evaluation-harness/references/benchmarks.md +394 -0
- package/skills/ai-research/11-evaluation/bigcode-evaluation-harness/references/custom-tasks.md +425 -0
- package/skills/ai-research/11-evaluation/bigcode-evaluation-harness/references/issues.md +395 -0
- package/skills/ai-research/11-evaluation/lm-evaluation-harness/SKILL.md +490 -0
- package/skills/ai-research/11-evaluation/lm-evaluation-harness/references/api-evaluation.md +491 -0
- package/skills/ai-research/11-evaluation/lm-evaluation-harness/references/benchmark-guide.md +489 -0
- package/skills/ai-research/11-evaluation/lm-evaluation-harness/references/custom-tasks.md +603 -0
- package/skills/ai-research/11-evaluation/lm-evaluation-harness/references/distributed-eval.md +520 -0
- package/skills/ai-research/11-evaluation/nemo-evaluator/SKILL.md +494 -0
- package/skills/ai-research/11-evaluation/nemo-evaluator/references/adapter-system.md +341 -0
- package/skills/ai-research/11-evaluation/nemo-evaluator/references/configuration.md +448 -0
- package/skills/ai-research/11-evaluation/nemo-evaluator/references/custom-benchmarks.md +316 -0
- package/skills/ai-research/11-evaluation/nemo-evaluator/references/execution-backends.md +362 -0
- package/skills/ai-research/12-inference-serving/llama-cpp/SKILL.md +258 -0
- package/skills/ai-research/12-inference-serving/llama-cpp/references/optimization.md +90 -0
- package/skills/ai-research/12-inference-serving/llama-cpp/references/quantization.md +214 -0
- package/skills/ai-research/12-inference-serving/llama-cpp/references/server.md +126 -0
- package/skills/ai-research/12-inference-serving/sglang/SKILL.md +442 -0
- package/skills/ai-research/12-inference-serving/sglang/references/deployment.md +491 -0
- package/skills/ai-research/12-inference-serving/sglang/references/radix-attention.md +414 -0
- package/skills/ai-research/12-inference-serving/sglang/references/structured-generation.md +542 -0
- package/skills/ai-research/12-inference-serving/tensorrt-llm/SKILL.md +187 -0
- package/skills/ai-research/12-inference-serving/tensorrt-llm/references/multi-gpu.md +299 -0
- package/skills/ai-research/12-inference-serving/tensorrt-llm/references/optimization.md +243 -0
- package/skills/ai-research/12-inference-serving/tensorrt-llm/references/serving.md +471 -0
- package/skills/ai-research/12-inference-serving/vllm/SKILL.md +364 -0
- package/skills/ai-research/12-inference-serving/vllm/references/optimization.md +227 -0
- package/skills/ai-research/12-inference-serving/vllm/references/quantization.md +285 -0
- package/skills/ai-research/12-inference-serving/vllm/references/server-deployment.md +256 -0
- package/skills/ai-research/12-inference-serving/vllm/references/troubleshooting.md +448 -0
- package/skills/ai-research/13-mlops/mlflow/SKILL.md +704 -0
- package/skills/ai-research/13-mlops/mlflow/references/deployment.md +745 -0
- package/skills/ai-research/13-mlops/mlflow/references/model-registry.md +771 -0
- package/skills/ai-research/13-mlops/mlflow/references/tracking.md +681 -0
- package/skills/ai-research/13-mlops/swanlab/SKILL.md +406 -0
- package/skills/ai-research/13-mlops/swanlab/references/integrations.md +305 -0
- package/skills/ai-research/13-mlops/swanlab/references/visualization.md +234 -0
- package/skills/ai-research/13-mlops/tensorboard/SKILL.md +629 -0
- package/skills/ai-research/13-mlops/tensorboard/references/integrations.md +639 -0
- package/skills/ai-research/13-mlops/tensorboard/references/profiling.md +546 -0
- package/skills/ai-research/13-mlops/tensorboard/references/visualization.md +621 -0
- package/skills/ai-research/13-mlops/weights-and-biases/SKILL.md +590 -0
- package/skills/ai-research/13-mlops/weights-and-biases/references/artifacts.md +585 -0
- package/skills/ai-research/13-mlops/weights-and-biases/references/integrations.md +701 -0
- package/skills/ai-research/13-mlops/weights-and-biases/references/sweeps.md +848 -0
- package/skills/ai-research/14-agents/a-evolve/SKILL.md +383 -0
- package/skills/ai-research/14-agents/a-evolve/references/README.md +831 -0
- package/skills/ai-research/14-agents/a-evolve/references/api.md +594 -0
- package/skills/ai-research/14-agents/a-evolve/references/architecture.md +417 -0
- package/skills/ai-research/14-agents/a-evolve/references/design-patterns.md +462 -0
- package/skills/ai-research/14-agents/a-evolve/references/examples.md +352 -0
- package/skills/ai-research/14-agents/a-evolve/references/issues.md +591 -0
- package/skills/ai-research/14-agents/a-evolve/references/releases.md +112 -0
- package/skills/ai-research/14-agents/a-evolve/references/tutorials.md +819 -0
- package/skills/ai-research/14-agents/autogpt/SKILL.md +403 -0
- package/skills/ai-research/14-agents/autogpt/references/advanced-usage.md +536 -0
- package/skills/ai-research/14-agents/autogpt/references/troubleshooting.md +421 -0
- package/skills/ai-research/14-agents/crewai/SKILL.md +498 -0
- package/skills/ai-research/14-agents/crewai/references/flows.md +439 -0
- package/skills/ai-research/14-agents/crewai/references/tools.md +430 -0
- package/skills/ai-research/14-agents/crewai/references/troubleshooting.md +481 -0
- package/skills/ai-research/14-agents/langchain/SKILL.md +480 -0
- package/skills/ai-research/14-agents/langchain/references/agents.md +500 -0
- package/skills/ai-research/14-agents/langchain/references/integration.md +563 -0
- package/skills/ai-research/14-agents/langchain/references/rag.md +601 -0
- package/skills/ai-research/14-agents/llamaindex/SKILL.md +569 -0
- package/skills/ai-research/14-agents/llamaindex/references/agents.md +84 -0
- package/skills/ai-research/14-agents/llamaindex/references/data_connectors.md +109 -0
- package/skills/ai-research/14-agents/llamaindex/references/query_engines.md +407 -0
- package/skills/ai-research/15-rag/chroma/SKILL.md +406 -0
- package/skills/ai-research/15-rag/chroma/references/integration.md +39 -0
- package/skills/ai-research/15-rag/faiss/SKILL.md +221 -0
- package/skills/ai-research/15-rag/faiss/references/index_types.md +281 -0
- package/skills/ai-research/15-rag/pinecone/SKILL.md +358 -0
- package/skills/ai-research/15-rag/pinecone/references/deployment.md +182 -0
- package/skills/ai-research/15-rag/qdrant/SKILL.md +493 -0
- package/skills/ai-research/15-rag/qdrant/references/advanced-usage.md +649 -0
- package/skills/ai-research/15-rag/qdrant/references/troubleshooting.md +632 -0
- package/skills/ai-research/15-rag/sentence-transformers/SKILL.md +255 -0
- package/skills/ai-research/15-rag/sentence-transformers/references/models.md +124 -0
- package/skills/ai-research/16-prompt-engineering/dspy/SKILL.md +590 -0
- package/skills/ai-research/16-prompt-engineering/dspy/references/examples.md +664 -0
- package/skills/ai-research/16-prompt-engineering/dspy/references/modules.md +476 -0
- package/skills/ai-research/16-prompt-engineering/dspy/references/optimizers.md +567 -0
- package/skills/ai-research/16-prompt-engineering/guidance/SKILL.md +572 -0
- package/skills/ai-research/16-prompt-engineering/guidance/references/backends.md +555 -0
- package/skills/ai-research/16-prompt-engineering/guidance/references/constraints.md +675 -0
- package/skills/ai-research/16-prompt-engineering/guidance/references/examples.md +768 -0
- package/skills/ai-research/16-prompt-engineering/instructor/SKILL.md +740 -0
- package/skills/ai-research/16-prompt-engineering/instructor/references/examples.md +108 -0
- package/skills/ai-research/16-prompt-engineering/instructor/references/providers.md +71 -0
- package/skills/ai-research/16-prompt-engineering/instructor/references/validation.md +607 -0
- package/skills/ai-research/16-prompt-engineering/outlines/SKILL.md +652 -0
- package/skills/ai-research/16-prompt-engineering/outlines/references/backends.md +616 -0
- package/skills/ai-research/16-prompt-engineering/outlines/references/examples.md +774 -0
- package/skills/ai-research/16-prompt-engineering/outlines/references/json_generation.md +653 -0
- package/skills/ai-research/17-observability/langsmith/SKILL.md +422 -0
- package/skills/ai-research/17-observability/langsmith/references/advanced-usage.md +549 -0
- package/skills/ai-research/17-observability/langsmith/references/troubleshooting.md +538 -0
- package/skills/ai-research/17-observability/phoenix/SKILL.md +475 -0
- package/skills/ai-research/17-observability/phoenix/references/advanced-usage.md +620 -0
- package/skills/ai-research/17-observability/phoenix/references/troubleshooting.md +539 -0
- package/skills/ai-research/18-multimodal/audiocraft/SKILL.md +564 -0
- package/skills/ai-research/18-multimodal/audiocraft/references/advanced-usage.md +667 -0
- package/skills/ai-research/18-multimodal/audiocraft/references/troubleshooting.md +505 -0
- package/skills/ai-research/18-multimodal/blip-2/SKILL.md +564 -0
- package/skills/ai-research/18-multimodal/blip-2/references/advanced-usage.md +681 -0
- package/skills/ai-research/18-multimodal/blip-2/references/troubleshooting.md +527 -0
- package/skills/ai-research/18-multimodal/clip/SKILL.md +253 -0
- package/skills/ai-research/18-multimodal/clip/references/applications.md +208 -0
- package/skills/ai-research/18-multimodal/cosmos-policy/SKILL.md +402 -0
- package/skills/ai-research/18-multimodal/cosmos-policy/references/libero-commands.md +175 -0
- package/skills/ai-research/18-multimodal/cosmos-policy/references/robocasa-commands.md +147 -0
- package/skills/ai-research/18-multimodal/llava/SKILL.md +304 -0
- package/skills/ai-research/18-multimodal/llava/references/training.md +198 -0
- package/skills/ai-research/18-multimodal/openpi/SKILL.md +480 -0
- package/skills/ai-research/18-multimodal/openpi/references/checkpoints-and-env-map.md +86 -0
- package/skills/ai-research/18-multimodal/openpi/references/config-recipes.md +50 -0
- package/skills/ai-research/18-multimodal/openpi/references/pytorch-gotchas.md +49 -0
- package/skills/ai-research/18-multimodal/openpi/references/remote-client-pattern.md +48 -0
- package/skills/ai-research/18-multimodal/openpi/references/training-debugging.md +46 -0
- package/skills/ai-research/18-multimodal/openvla-oft/SKILL.md +441 -0
- package/skills/ai-research/18-multimodal/openvla-oft/references/aloha-workflow.md +141 -0
- package/skills/ai-research/18-multimodal/openvla-oft/references/config-troubleshooting.md +97 -0
- package/skills/ai-research/18-multimodal/openvla-oft/references/libero-workflow.md +115 -0
- package/skills/ai-research/18-multimodal/openvla-oft/references/paper-and-checkpoints.md +84 -0
- package/skills/ai-research/18-multimodal/segment-anything/SKILL.md +500 -0
- package/skills/ai-research/18-multimodal/segment-anything/references/advanced-usage.md +590 -0
- package/skills/ai-research/18-multimodal/segment-anything/references/troubleshooting.md +485 -0
- package/skills/ai-research/18-multimodal/stable-diffusion/SKILL.md +519 -0
- package/skills/ai-research/18-multimodal/stable-diffusion/references/advanced-usage.md +717 -0
- package/skills/ai-research/18-multimodal/stable-diffusion/references/troubleshooting.md +556 -0
- package/skills/ai-research/18-multimodal/whisper/SKILL.md +317 -0
- package/skills/ai-research/18-multimodal/whisper/references/languages.md +190 -0
- package/skills/ai-research/19-emerging-techniques/knowledge-distillation/SKILL.md +458 -0
- package/skills/ai-research/19-emerging-techniques/knowledge-distillation/references/minillm.md +335 -0
- package/skills/ai-research/19-emerging-techniques/long-context/SKILL.md +536 -0
- package/skills/ai-research/19-emerging-techniques/long-context/references/extension_methods.md +469 -0
- package/skills/ai-research/19-emerging-techniques/long-context/references/fine_tuning.md +612 -0
- package/skills/ai-research/19-emerging-techniques/long-context/references/rope.md +403 -0
- package/skills/ai-research/19-emerging-techniques/model-merging/SKILL.md +497 -0
- package/skills/ai-research/19-emerging-techniques/model-merging/references/coefficient-tuning.md +415 -0
- package/skills/ai-research/19-emerging-techniques/model-merging/references/evaluation.md +463 -0
- package/skills/ai-research/19-emerging-techniques/model-merging/references/examples.md +429 -0
- package/skills/ai-research/19-emerging-techniques/model-merging/references/methods.md +353 -0
- package/skills/ai-research/19-emerging-techniques/model-pruning/SKILL.md +495 -0
- package/skills/ai-research/19-emerging-techniques/model-pruning/references/wanda.md +348 -0
- package/skills/ai-research/19-emerging-techniques/moe-training/SKILL.md +526 -0
- package/skills/ai-research/19-emerging-techniques/moe-training/references/architectures.md +433 -0
- package/skills/ai-research/19-emerging-techniques/moe-training/references/inference.md +349 -0
- package/skills/ai-research/19-emerging-techniques/moe-training/references/training.md +426 -0
- package/skills/ai-research/19-emerging-techniques/speculative-decoding/SKILL.md +467 -0
- package/skills/ai-research/19-emerging-techniques/speculative-decoding/references/lookahead.md +310 -0
- package/skills/ai-research/19-emerging-techniques/speculative-decoding/references/medusa.md +351 -0
- package/skills/ai-research/20-ml-paper-writing/academic-plotting/SKILL.md +479 -0
- package/skills/ai-research/20-ml-paper-writing/academic-plotting/references/data-visualization.md +509 -0
- package/skills/ai-research/20-ml-paper-writing/academic-plotting/references/diagram-generation.md +395 -0
- package/skills/ai-research/20-ml-paper-writing/academic-plotting/references/style-guide.md +305 -0
- package/skills/ai-research/20-ml-paper-writing/ml-paper-writing/SKILL.md +982 -0
- package/skills/ai-research/20-ml-paper-writing/ml-paper-writing/references/checklists.md +348 -0
- package/skills/ai-research/20-ml-paper-writing/ml-paper-writing/references/citation-workflow.md +563 -0
- package/skills/ai-research/20-ml-paper-writing/ml-paper-writing/references/reviewer-guidelines.md +349 -0
- package/skills/ai-research/20-ml-paper-writing/ml-paper-writing/references/sources.md +165 -0
- package/skills/ai-research/20-ml-paper-writing/ml-paper-writing/references/writing-guide.md +477 -0
- package/skills/ai-research/20-ml-paper-writing/presenting-conference-talks/SKILL.md +298 -0
- package/skills/ai-research/20-ml-paper-writing/presenting-conference-talks/references/slide-templates.md +505 -0
- package/skills/ai-research/20-ml-paper-writing/systems-paper-writing/SKILL.md +270 -0
- package/skills/ai-research/20-ml-paper-writing/systems-paper-writing/references/checklist.md +279 -0
- package/skills/ai-research/20-ml-paper-writing/systems-paper-writing/references/reviewer-guidelines.md +116 -0
- package/skills/ai-research/20-ml-paper-writing/systems-paper-writing/references/section-blueprints.md +400 -0
- package/skills/ai-research/20-ml-paper-writing/systems-paper-writing/references/systems-conferences.md +261 -0
- package/skills/ai-research/20-ml-paper-writing/systems-paper-writing/references/writing-patterns.md +231 -0
- package/skills/ai-research/21-research-ideation/brainstorming-research-ideas/SKILL.md +384 -0
- package/skills/ai-research/21-research-ideation/creative-thinking-for-research/SKILL.md +366 -0
- package/skills/ai-research/22-agent-native-research-artifact/compiler/SKILL.md +245 -0
- package/skills/ai-research/22-agent-native-research-artifact/compiler/references/ara-schema.md +439 -0
- package/skills/ai-research/22-agent-native-research-artifact/compiler/references/exploration-tree-spec.md +125 -0
- package/skills/ai-research/22-agent-native-research-artifact/compiler/references/validation-checklist.md +149 -0
- package/skills/ai-research/22-agent-native-research-artifact/research-manager/SKILL.md +324 -0
- package/skills/ai-research/22-agent-native-research-artifact/research-manager/references/event-taxonomy.md +124 -0
- package/skills/ai-research/22-agent-native-research-artifact/research-manager/references/provenance-tags.md +190 -0
- package/skills/ai-research/22-agent-native-research-artifact/research-manager/references/session-protocol.md +162 -0
- package/skills/ai-research/22-agent-native-research-artifact/rigor-reviewer/SKILL.md +322 -0
- package/skills/ai-research/22-agent-native-research-artifact/rigor-reviewer/references/review-dimensions.md +182 -0
- package/skills/ai-research/SKILL.md +101 -0
- package/skills/ai-research/SOURCE +38 -0
- package/skills/ai-research/index/1-frame.md +18 -0
- package/skills/ai-research/index/2-data.md +21 -0
- package/skills/ai-research/index/3-train.md +48 -0
- package/skills/ai-research/index/4-measure.md +25 -0
- package/skills/ai-research/index/5-run.md +23 -0
- package/skills/ai-research/index/6-write.md +35 -0
- package/skills/ai-research/library.json +3456 -0
- package/skills/analyze-app/EVALS.md +82 -0
- package/skills/analyze-app/SKILL.md +188 -0
- package/skills/design-contract/EVALS.md +80 -0
- package/skills/design-contract/SKILL.md +171 -0
- package/skills/design-library/EVALS.md +85 -0
- package/skills/design-library/INDEX.md +59 -0
- package/skills/design-library/SKILL.md +58 -0
- package/skills/design-library/SOURCE +131 -0
- package/skills/design-library/adaptive-interfaces/skills/colour-independence/SKILL.md +90 -0
- package/skills/design-library/adaptive-interfaces/skills/flexible-typography/SKILL.md +83 -0
- package/skills/design-library/adaptive-interfaces/skills/information-density/SKILL.md +87 -0
- package/skills/design-library/adaptive-interfaces/skills/preference-audit/SKILL.md +70 -0
- package/skills/design-library/adaptive-interfaces/skills/responsive-accessibility/SKILL.md +86 -0
- package/skills/design-library/adaptive-interfaces/skills/responsive-review/SKILL.md +76 -0
- package/skills/design-library/adaptive-interfaces/skills/simplified-views/SKILL.md +97 -0
- package/skills/design-library/adaptive-interfaces/skills/specify/SKILL.md +63 -0
- package/skills/design-library/adaptive-interfaces/skills/user-preference-respect/SKILL.md +100 -0
- package/skills/design-library/cognitive-accessibility/skills/adaptive-personalisation/SKILL.md +61 -0
- package/skills/design-library/cognitive-accessibility/skills/assess-load/SKILL.md +48 -0
- package/skills/design-library/cognitive-accessibility/skills/cognitive-load-assessment/SKILL.md +60 -0
- package/skills/design-library/cognitive-accessibility/skills/contextual-help-design/SKILL.md +61 -0
- package/skills/design-library/cognitive-accessibility/skills/error-prevention-recovery/SKILL.md +71 -0
- package/skills/design-library/cognitive-accessibility/skills/focus-attention-design/SKILL.md +67 -0
- package/skills/design-library/cognitive-accessibility/skills/memory-load-reduction/SKILL.md +60 -0
- package/skills/design-library/cognitive-accessibility/skills/plain-language-design/SKILL.md +78 -0
- package/skills/design-library/cognitive-accessibility/skills/review/SKILL.md +56 -0
- package/skills/design-library/cognitive-accessibility/skills/simplify/SKILL.md +39 -0
- package/skills/design-library/cognitive-accessibility/skills/wayfinding-navigation/SKILL.md +64 -0
- package/skills/design-library/design-research/skills/behavioural-analytics/SKILL.md +33 -0
- package/skills/design-library/design-research/skills/qual-quant-triangulation/SKILL.md +33 -0
- package/skills/design-library/inclusive-interaction/skills/audit/SKILL.md +50 -0
- package/skills/design-library/inclusive-interaction/skills/design-flow/SKILL.md +58 -0
- package/skills/design-library/inclusive-interaction/skills/feedback-and-status/SKILL.md +76 -0
- package/skills/design-library/inclusive-interaction/skills/gesture-alternatives/SKILL.md +70 -0
- package/skills/design-library/inclusive-interaction/skills/keyboard-navigation/SKILL.md +99 -0
- package/skills/design-library/inclusive-interaction/skills/keyboard-review/SKILL.md +54 -0
- package/skills/design-library/inclusive-interaction/skills/motion-sensitivity/SKILL.md +79 -0
- package/skills/design-library/inclusive-interaction/skills/multi-modal-input/SKILL.md +70 -0
- package/skills/design-library/inclusive-interaction/skills/touch-target-design/SKILL.md +71 -0
- package/skills/design-library/inclusive-interaction/skills/voice-interaction/SKILL.md +68 -0
- package/skills/design-library/visual-critique/skills/critique-affordance/SKILL.md +45 -0
- package/skills/design-library/visual-critique/skills/critique-brand-consistency/SKILL.md +42 -0
- package/skills/design-library/visual-critique/skills/critique-color/SKILL.md +44 -0
- package/skills/design-library/visual-critique/skills/critique-composition/SKILL.md +46 -0
- package/skills/design-library/visual-critique/skills/critique-information-density/SKILL.md +45 -0
- package/skills/design-library/visual-critique/skills/critique-typography/SKILL.md +45 -0
- package/skills/design-library/visual-critique/skills/critique-visual-hierarchy/SKILL.md +40 -0
- package/skills/feature-audit/EVALS.md +83 -0
- package/skills/feature-audit/SKILL.md +326 -0
- package/skills/harness/EVALS.md +81 -0
- package/skills/harness/SKILL.md +88 -0
- package/skills/i-have-adhd/EVALS.md +80 -0
- package/skills/i-have-adhd/SKILL.md +183 -0
- package/skills/no-ai-slop/EVALS.md +89 -0
- package/skills/no-ai-slop/SKILL.md +209 -0
- package/skills/no-ai-slop/eval.md +52 -0
- package/skills/plan-add/EVALS.md +79 -0
- package/skills/plan-add/SKILL.md +179 -0
- package/skills/plan-add/render_table.py +72 -0
- package/skills/plan-status/EVALS.md +78 -0
- package/skills/plan-status/SKILL.md +52 -0
- package/skills/plan-sync/EVALS.md +80 -0
- package/skills/plan-sync/SKILL.md +69 -0
- package/skills/pm-frameworks/SKILL.md +33 -0
- package/skills/pm-frameworks/SOURCE +39 -0
- package/skills/pm-frameworks/intended-vs-implemented/SKILL.md +42 -0
- package/skills/pm-frameworks/opportunity-solution-tree/SKILL.md +64 -0
- package/skills/ponytail/EVALS.md +80 -0
- package/skills/ponytail/SKILL.md +124 -0
- package/skills/product-analysis/EVALS.md +103 -0
- package/skills/product-analysis/SKILL.md +361 -0
- package/skills/research/EVALS.md +104 -0
- package/skills/research/SKILL.md +570 -0
- package/skills/rl-env/SKILL.md +53 -0
- package/skills/rl-env/SOURCE +41 -0
- package/skills/rl-env/generate-nemo-gym-env/SKILL.md +169 -0
- package/skills/rl-env/generate-nemo-gym-env/references/architecture.md +114 -0
- package/skills/rl-env/generate-openenv-env/SKILL.md +183 -0
- package/skills/rl-env/generate-openenv-env/references/architecture.md +82 -0
- package/skills/rl-env/generate-ors-env/SKILL.md +209 -0
- package/skills/rl-env/generate-ors-env/references/architecture.md +113 -0
- package/skills/rl-env/generate-verifiers-env/SKILL.md +188 -0
- package/skills/rl-env/generate-verifiers-env/references/architecture.md +119 -0
- package/skills/rl-env/rl-env-from-description/SKILL.md +124 -0
- package/skills/rl-env/rl-env-from-description/references/interview.md +75 -0
- package/skills/rl-env/rl-env-from-description/references/nemo_gym.md +41 -0
- package/skills/rl-env/rl-env-from-description/references/openenv.md +35 -0
- package/skills/rl-env/rl-env-from-description/references/ors.md +50 -0
- package/skills/rl-env/rl-env-from-description/references/verifiers.md +39 -0
- package/skills/tezgah-contract/EVALS.md +78 -0
- package/skills/tezgah-contract/SKILL.md +792 -0
- package/statusline.py +129 -0
- package/workflows/graph-impact.js +153 -0
- package/workflows/graph-map.js +158 -0
- package/workflows/graph-review.js +170 -0
package/bin/consult
ADDED
|
@@ -0,0 +1,797 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""consult - second opinion from external models.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
consult "question" independent answers, then one referee
|
|
6
|
+
consult - read the question from redirected stdin
|
|
7
|
+
consult --use M1[,M2] [--judge M] record which members answer (no question: record only)
|
|
8
|
+
consult "question" --no-referee panel only, no cross-examination
|
|
9
|
+
consult "question" --judge MEMBER referee (default: the recorded judge, else an
|
|
10
|
+
available member outside the panel, else a panel
|
|
11
|
+
member not on this session's model, named in a note)
|
|
12
|
+
consult "question" --provider deepseek one HTTP provider for this run, not the record
|
|
13
|
+
consult "question" --models m1,m2 that provider's models for this run
|
|
14
|
+
consult "question" --online enable live web search (:online, OpenRouter)
|
|
15
|
+
consult "question" --timeout 120 per-member timeout seconds
|
|
16
|
+
|
|
17
|
+
Members (what --use records into ~/.config/tezgah/config.json "consult"):
|
|
18
|
+
cli:<name>[:<model>] a locally installed agent CLI, asked non-interactively:
|
|
19
|
+
omp, claude, codex, opencode, cursor-agent
|
|
20
|
+
<provider>[:<model>] an HTTP provider; bare, it is that provider's default panel
|
|
21
|
+
openrouter OPENROUTER_API_KEY ~/.config/openrouter/key
|
|
22
|
+
deepseek DEEPSEEK_API_KEY ~/.config/deepseek/key
|
|
23
|
+
inception INCEPTION_API_KEY ~/.config/inception/key
|
|
24
|
+
Key lookup order: the provider's env var, then its key file. CONSULT_URL
|
|
25
|
+
overrides the endpoint (a gateway, or a test server), CONSULT_MODELS the
|
|
26
|
+
OpenRouter panel for one run, CONSULT_JUDGE the referee. The referee sees the
|
|
27
|
+
answers as "ANSWER 1..n" in a shuffled order and the output maps each label to
|
|
28
|
+
its member; CONSULT_SEED fixes that order (tests).
|
|
29
|
+
|
|
30
|
+
CONSULT_SESSION_MODEL names the model this session itself runs on when a host
|
|
31
|
+
hook knows it; otherwise, on an omp session (OMPCODE), consult reads omp's own
|
|
32
|
+
`modelRoles.default`. A recorded member whose model is that one, or another
|
|
33
|
+
model of the same family
|
|
34
|
+
(DeepSeek, Claude, GPT, ...), is skipped - a second opinion from the model
|
|
35
|
+
already answering, or its sibling, is the weakest one on offer - and the
|
|
36
|
+
referee is never it. `--use` naming that member in the same run overrides the
|
|
37
|
+
skip. `cli:claude` and `cli:codex` belong to their vendor's family; a CLI member
|
|
38
|
+
whose configured model consult cannot read (bare `cli:omp`) counts as unknown,
|
|
39
|
+
not as the session's, and is named as such.
|
|
40
|
+
|
|
41
|
+
With no --provider/--models/--online the recorded members answer. When every
|
|
42
|
+
recorded member fails, OpenRouter's default panel is the last fallback if its
|
|
43
|
+
key is present and it was not among them. An agent CLI runs in an empty temp
|
|
44
|
+
dir with TEZGAH_NESTED=1 (its tezgah hooks skip the reply-shape rules, since
|
|
45
|
+
consult reads the answer), so the only context it sees is the question.
|
|
46
|
+
|
|
47
|
+
Output: one "## <member>" section per independent answer, then "## referee
|
|
48
|
+
(<member>)" judging them, with the label each answer carried, then a footer
|
|
49
|
+
naming what answered, what failed and how, and the one variable to change on a
|
|
50
|
+
retry. A reply that does not answer under the five headings the referee was
|
|
51
|
+
asked for is reported as a failed cross-examination, not printed as a judgement.
|
|
52
|
+
|
|
53
|
+
Exit codes: 0 at least one member answered; 1 misuse (bad or unknown arguments,
|
|
54
|
+
an unavailable --use member, or --online on a provider that has no :online
|
|
55
|
+
suffix); 2 no key for the chosen provider, or no member available at all; 3 the
|
|
56
|
+
--provider/--models run got no answer; 4 no recorded choice - one "offer:" line
|
|
57
|
+
per available member, nothing was asked; 5 every recorded member (and the
|
|
58
|
+
fallback) failed - one "reoffer:" line per member still available that did not
|
|
59
|
+
fail. On 4 or 5, ask the user which to use and record it with --use.
|
|
60
|
+
"""
|
|
61
|
+
import concurrent.futures
|
|
62
|
+
import json
|
|
63
|
+
import os
|
|
64
|
+
import random
|
|
65
|
+
import re
|
|
66
|
+
import shutil
|
|
67
|
+
import signal
|
|
68
|
+
import subprocess
|
|
69
|
+
import sys
|
|
70
|
+
import tempfile
|
|
71
|
+
import urllib.error
|
|
72
|
+
import urllib.request
|
|
73
|
+
|
|
74
|
+
HERE = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
|
75
|
+
sys.path.insert(0, os.path.join(HERE, "hooks"))
|
|
76
|
+
import tezgah_paths as tp # noqa: E402
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# The module's one opener: a redirect that leaves the endpoint's host is still
|
|
80
|
+
# followed, but without the bearer token (the audit's L-9), so a repointed
|
|
81
|
+
# CONSULT_URL cannot hand the provider key to whatever host the answer named; a
|
|
82
|
+
# host that needs it answers 401 and the run reports a key failure.
|
|
83
|
+
OPENER = tp.guarded_opener(drop_auth=True)
|
|
84
|
+
|
|
85
|
+
# provider -> (chat-completions URL, key env var, key file, default models)
|
|
86
|
+
PROVIDERS = {
|
|
87
|
+
"openrouter": (
|
|
88
|
+
"https://openrouter.ai/api/v1/chat/completions",
|
|
89
|
+
"OPENROUTER_API_KEY",
|
|
90
|
+
"~/.config/openrouter/key",
|
|
91
|
+
"openai/gpt-6.1-sol,google/gemini-3.8-flash",
|
|
92
|
+
),
|
|
93
|
+
"deepseek": (
|
|
94
|
+
"https://api.deepseek.com/v1/chat/completions",
|
|
95
|
+
"DEEPSEEK_API_KEY",
|
|
96
|
+
"~/.config/deepseek/key",
|
|
97
|
+
"deepseek-chat,deepseek-reasoner",
|
|
98
|
+
),
|
|
99
|
+
# Inception Labs serves an OpenAI-compatible chat-completions route; the
|
|
100
|
+
# model ids carry no vendor prefix here (`mercury-2.5`, not
|
|
101
|
+
# `inception/mercury-2.5`, which is the OpenRouter spelling).
|
|
102
|
+
"inception": (
|
|
103
|
+
"https://api.inceptionlabs.ai/v1/chat/completions",
|
|
104
|
+
"INCEPTION_API_KEY",
|
|
105
|
+
"~/.config/inception/key",
|
|
106
|
+
"mercury-2.5,mercury-2",
|
|
107
|
+
),
|
|
108
|
+
}
|
|
109
|
+
SYSTEM = (
|
|
110
|
+
"You are one of several independent models consulted for a second opinion "
|
|
111
|
+
"on an engineering decision. Be direct and specific. State your "
|
|
112
|
+
"recommendation first, then the key reasons, then what could make it "
|
|
113
|
+
"wrong. If you lack context to judge, say exactly what is missing. End "
|
|
114
|
+
"with 'What would change my mind:' and name the observation or test."
|
|
115
|
+
)
|
|
116
|
+
# The second stage. Its whole job is the part one-shot parallel querying cannot
|
|
117
|
+
# do: make the disagreement explicit instead of leaving the caller to diff two
|
|
118
|
+
# answers. The headings are fixed so the caller can read back named fields
|
|
119
|
+
# rather than a free-form verdict that quietly drops the minority view.
|
|
120
|
+
REFEREE = (
|
|
121
|
+
"You are the referee for a panel of independent models that answered the "
|
|
122
|
+
"same question without seeing each other's answers. Judge their answers "
|
|
123
|
+
"against the question's own constraints. Do not average them into a bland "
|
|
124
|
+
"compromise and do not invent a consensus that is not there.\n"
|
|
125
|
+
"Answer under exactly these headings, in this order:\n"
|
|
126
|
+
"1. Recommendation - the single answer you would act on, and why.\n"
|
|
127
|
+
"2. Key disagreements - where the panel actually conflicts, naming which "
|
|
128
|
+
"answer holds which position.\n"
|
|
129
|
+
"3. Unchecked assumptions - what every answer assumed and none verified.\n"
|
|
130
|
+
"4. What would change my mind - the observation, test or evidence that "
|
|
131
|
+
"flips the recommendation.\n"
|
|
132
|
+
"5. Requested evidence - what you would need to read, run or fetch to "
|
|
133
|
+
"settle the disagreement.\n"
|
|
134
|
+
"If one answer is simply better, say so plainly. If the panel agrees, say "
|
|
135
|
+
"what they agreed on and what that agreement does not prove."
|
|
136
|
+
)
|
|
137
|
+
# The five field names REFEREE above asks for, as substrings a reply has to
|
|
138
|
+
# carry. The headings are why stage two exists - the caller reads back named
|
|
139
|
+
# fields instead of a paraphrase that quietly drops the minority view - so a
|
|
140
|
+
# reply without them is not a cross-examination and is never printed as one.
|
|
141
|
+
# Presence only: whether the verdict is *right* is the caller's to judge, and a
|
|
142
|
+
# second judge here would be exactly the free-form opinion this stage replaces.
|
|
143
|
+
REFEREE_FIELDS = ("recommendation", "key disagreements", "unchecked assumptions",
|
|
144
|
+
"what would change my mind", "requested evidence")
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
# What an offer line tells the user each HTTP member answers with; a CLI's
|
|
148
|
+
# family is in tp.CONSULT_CLIS.
|
|
149
|
+
FAMILY = {"openrouter": "OpenAI GPT + Google Gemini through OpenRouter (the default panel)",
|
|
150
|
+
"deepseek": "DeepSeek", "inception": "Inception Mercury"}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def defaults(provider):
|
|
154
|
+
if provider == "openrouter" and os.environ.get("CONSULT_MODELS"):
|
|
155
|
+
return os.environ["CONSULT_MODELS"].split(",")
|
|
156
|
+
return PROVIDERS[provider][3].split(",")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def endpoint(provider):
|
|
160
|
+
return os.environ.get("CONSULT_URL", "").strip() or PROVIDERS[provider][0]
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def key_for(provider):
|
|
164
|
+
"""The provider's key from its env var, then its key file, else None."""
|
|
165
|
+
_url, env, path, _models = PROVIDERS[provider]
|
|
166
|
+
k = os.environ.get(env, "").strip()
|
|
167
|
+
if not k:
|
|
168
|
+
try:
|
|
169
|
+
with open(os.path.expanduser(path), encoding="utf-8") as fh:
|
|
170
|
+
k = fh.read().strip()
|
|
171
|
+
except OSError:
|
|
172
|
+
pass
|
|
173
|
+
return k or None
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def no_key(provider):
|
|
177
|
+
_url, env, path, _models = PROVIDERS[provider]
|
|
178
|
+
return "no API key for %s. Set %s or write the key to %s (chmod 600)." % (
|
|
179
|
+
provider, env, path)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def classify(exc):
|
|
183
|
+
"""A failure class, named so the footer can point at ONE variable."""
|
|
184
|
+
if isinstance(exc, urllib.error.HTTPError):
|
|
185
|
+
# 402 is an empty account, not a refused key: naming it http-4xx sent
|
|
186
|
+
# the caller to rotate a key that was fine while the credit stayed out.
|
|
187
|
+
return "credit" if exc.code == 402 else "http-%d" % exc.code
|
|
188
|
+
if isinstance(exc, TimeoutError):
|
|
189
|
+
# A stalled response, as distinct from the wall-clock deadline below:
|
|
190
|
+
# urllib wraps OSError around the request only, not around getresponse
|
|
191
|
+
# or the body read, so a slow stream surfaces here rather than there.
|
|
192
|
+
return "timeout"
|
|
193
|
+
if isinstance(exc, urllib.error.URLError):
|
|
194
|
+
return "network"
|
|
195
|
+
if isinstance(exc, (ValueError, KeyError, IndexError, TypeError)):
|
|
196
|
+
# json.JSONDecodeError is a ValueError; a reply missing choices[0] is a
|
|
197
|
+
# schema mismatch, not a model verdict.
|
|
198
|
+
return "malformed-reply"
|
|
199
|
+
return "error"
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def ask(provider, model, question, key, timeout, system):
|
|
203
|
+
"""One stateless chat completion. Returns (model, answer, err, class).
|
|
204
|
+
|
|
205
|
+
A plain-http endpoint off this machine is refused before any request: the
|
|
206
|
+
bearer key and the packet would cross the network in clear (`tp.plain_http`,
|
|
207
|
+
as `bin/codegen` does; loopback stays allowed for the tests' fake)."""
|
|
208
|
+
url = endpoint(provider)
|
|
209
|
+
if tp.plain_http(url):
|
|
210
|
+
return (model, None, "CONSULT_URL %s is plain http off this machine: the key "
|
|
211
|
+
"and the packet would cross the network in clear" % url, "plain-http")
|
|
212
|
+
body = json.dumps({
|
|
213
|
+
"model": model.strip(),
|
|
214
|
+
"messages": [
|
|
215
|
+
{"role": "system", "content": system},
|
|
216
|
+
{"role": "user", "content": question},
|
|
217
|
+
],
|
|
218
|
+
}).encode()
|
|
219
|
+
try:
|
|
220
|
+
# The construction is inside the try on purpose: Request() parses the
|
|
221
|
+
# URL and raises ValueError for a scheme-less one (e.g. a CONSULT_URL
|
|
222
|
+
# of "localhost"), which would otherwise escape this worker as an
|
|
223
|
+
# uncaught exception and kill the run with a traceback.
|
|
224
|
+
req = urllib.request.Request(url, data=body, headers={
|
|
225
|
+
"Authorization": "Bearer " + key,
|
|
226
|
+
"Content-Type": "application/json",
|
|
227
|
+
})
|
|
228
|
+
with OPENER.open(req, timeout=timeout) as resp:
|
|
229
|
+
data = json.load(resp)
|
|
230
|
+
answer = data["choices"][0]["message"]["content"]
|
|
231
|
+
except Exception as exc: # network, HTTP, schema - all reported, never raised
|
|
232
|
+
detail = ""
|
|
233
|
+
if hasattr(exc, "read"):
|
|
234
|
+
try:
|
|
235
|
+
detail = " " + exc.read().decode()[:300]
|
|
236
|
+
except Exception:
|
|
237
|
+
pass
|
|
238
|
+
return model, None, "%s%s" % (exc, detail), classify(exc)
|
|
239
|
+
if not (answer or "").strip():
|
|
240
|
+
# A model can return 200 with null/empty content (refusal, filtered
|
|
241
|
+
# response). That is a failure to answer, not an answer -- and
|
|
242
|
+
# crashing here used to swallow every model after it in the list.
|
|
243
|
+
return model, None, "empty response (refused or filtered)", "empty"
|
|
244
|
+
return model, answer.strip(), None, None
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def ask_cli(name, model, prompt, timeout):
|
|
248
|
+
"""One non-interactive run of an agent CLI. Returns (answer, err, class).
|
|
249
|
+
|
|
250
|
+
The child starts in an empty temp dir with stdin closed: it cannot read the
|
|
251
|
+
repository the question is about, so the answer rests on the packet alone,
|
|
252
|
+
and a CLI that wants a login fails instead of waiting for input. It runs in
|
|
253
|
+
its own process group so a timeout kills the tools it spawned too."""
|
|
254
|
+
exe = tp.which_user(name)
|
|
255
|
+
if not exe:
|
|
256
|
+
return None, "%s is not installed" % name, "missing"
|
|
257
|
+
base, flag, _family = tp.CONSULT_CLIS[name]
|
|
258
|
+
argv = [exe] + base[1:] + ([flag, model] if model else []) + [prompt]
|
|
259
|
+
cwd = tempfile.mkdtemp(prefix="consult-")
|
|
260
|
+
try:
|
|
261
|
+
proc = subprocess.Popen(argv, cwd=cwd, stdin=subprocess.DEVNULL,
|
|
262
|
+
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
|
263
|
+
text=True, start_new_session=True,
|
|
264
|
+
env=dict(os.environ, TEZGAH_NESTED="1"))
|
|
265
|
+
try:
|
|
266
|
+
out, err = proc.communicate(timeout=timeout)
|
|
267
|
+
except subprocess.TimeoutExpired:
|
|
268
|
+
try:
|
|
269
|
+
os.killpg(proc.pid, signal.SIGKILL)
|
|
270
|
+
except (AttributeError, OSError): # no process groups (Windows)
|
|
271
|
+
proc.kill()
|
|
272
|
+
return None, "no answer within %ss" % timeout, "timeout"
|
|
273
|
+
except OSError as exc:
|
|
274
|
+
return None, str(exc), "missing"
|
|
275
|
+
finally:
|
|
276
|
+
shutil.rmtree(cwd, ignore_errors=True)
|
|
277
|
+
if proc.returncode:
|
|
278
|
+
# the tail: a CLI prints its banner first and the reason it failed last
|
|
279
|
+
return (None, "exit %d: %s" % (proc.returncode, (err or out).strip()[-300:]),
|
|
280
|
+
"cli-exit-%d" % proc.returncode)
|
|
281
|
+
if not out.strip():
|
|
282
|
+
return None, "empty output", "empty"
|
|
283
|
+
return out.strip(), None, None
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def parse(member):
|
|
287
|
+
"""`cli:<name>[:<model>]` or `<provider>[:<model>]` as (kind, name, model),
|
|
288
|
+
or None for a name consult does not know."""
|
|
289
|
+
if member.startswith("cli:"):
|
|
290
|
+
name, _, model = member[4:].partition(":")
|
|
291
|
+
return ("cli", name, model or None) if name in tp.CONSULT_CLIS else None
|
|
292
|
+
name, _, model = member.partition(":")
|
|
293
|
+
return ("http", name, model or None) if name in PROVIDERS else None
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def option(parsed):
|
|
297
|
+
"""The offer a parsed member belongs to: `cli:<name>` or the provider."""
|
|
298
|
+
kind, name, _model = parsed
|
|
299
|
+
return "cli:" + name if kind == "cli" else name
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def calls(member):
|
|
303
|
+
"""The panel calls one member stands for: (label, option, kind, name, model).
|
|
304
|
+
A bare HTTP provider is its whole default panel; a label is what the output
|
|
305
|
+
names - the model id for HTTP, the member itself for a CLI."""
|
|
306
|
+
kind, name, model = parsed = parse(member)
|
|
307
|
+
if kind == "cli":
|
|
308
|
+
return [("cli:%s%s" % (name, ":" + model if model else ""), option(parsed),
|
|
309
|
+
kind, name, model)]
|
|
310
|
+
return [(m.strip(), name, kind, name, m.strip())
|
|
311
|
+
for m in ([model] if model else defaults(name))]
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def ask_call(call, question, timeout, system):
|
|
315
|
+
"""One panel or referee call, HTTP or CLI: (label, answer, err, class)."""
|
|
316
|
+
label, _option, kind, name, model = call
|
|
317
|
+
if kind == "cli":
|
|
318
|
+
return (label,) + ask_cli(name, model, system + "\n\n" + question, timeout)
|
|
319
|
+
key = key_for(name)
|
|
320
|
+
if not key:
|
|
321
|
+
return label, None, no_key(name), "key"
|
|
322
|
+
return (label,) + ask(name, model, question, key, timeout, system)[1:]
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def own_cli():
|
|
326
|
+
"""The calling host's own CLI member, when its env names it (omp also sets
|
|
327
|
+
CLAUDECODE, so OMPCODE is read first), else None."""
|
|
328
|
+
if os.environ.get("OMPCODE"):
|
|
329
|
+
return "cli:omp"
|
|
330
|
+
return "cli:claude" if os.environ.get("CLAUDECODE") else None
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def session_model():
|
|
334
|
+
"""The model this session itself runs on, or None when it cannot be read.
|
|
335
|
+
|
|
336
|
+
CONSULT_SESSION_MODEL first: a host hook can set it, and a pinned value
|
|
337
|
+
outranks the machine's own omp record. Otherwise, on omp only (OMPCODE, the
|
|
338
|
+
check `own_cli` makes), omp's own answer - `modelRoles.default`, read the way
|
|
339
|
+
`hooks/tezgah_models.py` reads it (same binary lookup, HOME as cwd so a
|
|
340
|
+
project layer cannot answer for it) - so a session that never set the knob is
|
|
341
|
+
still recognized as running on it. On another host omp's default is not this
|
|
342
|
+
session's model, and reading it there skipped members for nothing. A missing
|
|
343
|
+
binary, a non-zero exit or any malformed output is None: consult never
|
|
344
|
+
raises here, it just applies no exclusion."""
|
|
345
|
+
pin = os.environ.get("CONSULT_SESSION_MODEL", "").strip()
|
|
346
|
+
if pin:
|
|
347
|
+
return pin
|
|
348
|
+
exe = tp.omp_bin() if os.environ.get("OMPCODE") else None
|
|
349
|
+
if not exe:
|
|
350
|
+
return None
|
|
351
|
+
env = dict(os.environ, PI_CODING_AGENT_DIR=os.path.join(tp.HOME, ".omp", "agent"))
|
|
352
|
+
try:
|
|
353
|
+
proc = subprocess.run([exe, "config", "get", "modelRoles", "--json"],
|
|
354
|
+
capture_output=True, text=True, timeout=30,
|
|
355
|
+
env=env, cwd=tp.HOME)
|
|
356
|
+
except (OSError, subprocess.SubprocessError):
|
|
357
|
+
return None
|
|
358
|
+
try:
|
|
359
|
+
value = json.loads(proc.stdout).get("value") if proc.returncode == 0 else None
|
|
360
|
+
default = (value or {}).get("default")
|
|
361
|
+
except (ValueError, AttributeError):
|
|
362
|
+
return None
|
|
363
|
+
return str(default).strip() if default else None
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def same_model(a, b):
|
|
367
|
+
"""Whether two model ids name the same model, as the session record and a
|
|
368
|
+
consult member spell it: case-insensitive, with an effort suffix (`:high`)
|
|
369
|
+
or OpenRouter's `:online` dropped, and a provider prefix (`openai/`) on
|
|
370
|
+
either side ignored when the ids otherwise agree."""
|
|
371
|
+
|
|
372
|
+
def key(model):
|
|
373
|
+
return (model or "").split(":")[0].strip().lower()
|
|
374
|
+
a, b = key(a), key(b)
|
|
375
|
+
return bool(a) and (a == b or a.endswith("/" + b) or b.endswith("/" + a))
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
# A model's family by a stem in its own name, whichever provider serves it:
|
|
379
|
+
# `deepseek/deepseek-flash` and the deepseek provider's `deepseek-chat` are one
|
|
380
|
+
# family, and so are `anthropic/claude-opus-5-5`, `claude-sonnet-5` behind
|
|
381
|
+
# OpenRouter and Bedrock's `us.anthropic.claude-sonnet-4`. A stem counts at the
|
|
382
|
+
# start of the last path segment or after a `.` or `-` in it, never inside a
|
|
383
|
+
# word. OpenAI's stems are narrow on purpose: `gpt-j` and `gpt-neox` are
|
|
384
|
+
# EleutherAI's, and the o-series counts only as the whole name's start (`o3`,
|
|
385
|
+
# `openai/o4-mini`), since `Marco-o1` and `Skywork-o1` are other vendors'.
|
|
386
|
+
# A name no stem knows falls back to a vendor path segment
|
|
387
|
+
# (`openai/dall-e`); a router segment (`openrouter/`) names no family.
|
|
388
|
+
FAMILY_STEMS = ((r"claude", "anthropic"),
|
|
389
|
+
(r"(?:chat)?gpt-?\d|gpt-oss|codex", "openai"),
|
|
390
|
+
(r"gemini|gemma", "google"), (r"deepseek", "deepseek"),
|
|
391
|
+
(r"glm", "zai"), (r"grok", "xai"), (r"mercury", "inception"),
|
|
392
|
+
(r"qwen", "qwen"), (r"llama", "meta"), (r"mistral", "mistral"),
|
|
393
|
+
(r"kimi", "moonshot"))
|
|
394
|
+
FAMILY_AT = [(re.compile(r"(?:^|[.-])(?:%s)" % stem), family)
|
|
395
|
+
for stem, family in FAMILY_STEMS] + [(re.compile(r"^o\d"), "openai")]
|
|
396
|
+
VENDOR_ALIAS = {"z-ai": "zai", "x-ai": "xai", "meta-llama": "meta",
|
|
397
|
+
"moonshotai": "moonshot", "mistralai": "mistral"}
|
|
398
|
+
# The CLIs that only ever answer with one vendor's models, configured or not.
|
|
399
|
+
CLI_FAMILY = {"claude": "anthropic", "codex": "openai"}
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def model_family(model):
|
|
403
|
+
"""The family a model id belongs to, or None when the id names none."""
|
|
404
|
+
parts = (model or "").split(":")[0].strip().lower().split("/")
|
|
405
|
+
for pattern, family in FAMILY_AT:
|
|
406
|
+
if pattern.search(parts[-1]):
|
|
407
|
+
return family
|
|
408
|
+
known = {family for _stem, family in FAMILY_STEMS}
|
|
409
|
+
return next((VENDOR_ALIAS.get(p, p) for p in parts[:-1]
|
|
410
|
+
if VENDOR_ALIAS.get(p, p) in known), None)
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def skip_reason(call, session):
|
|
414
|
+
"""Why this panel call is not independent of the session, or None.
|
|
415
|
+
|
|
416
|
+
"model" when it runs on the session's own model, else the family both share:
|
|
417
|
+
a sibling model of the one under test shares its training lineage, so it is
|
|
418
|
+
a weak second opinion too (routing-gaps, "What this does not show"). A call
|
|
419
|
+
whose model and family are both unknown (a bare `cli:omp`) is NOT the
|
|
420
|
+
session's: unknown and equal are different answers."""
|
|
421
|
+
if not session:
|
|
422
|
+
return None
|
|
423
|
+
if call[4] and same_model(call[4], session):
|
|
424
|
+
return "model"
|
|
425
|
+
family = model_family(call[4]) or (CLI_FAMILY.get(call[3]) if call[2] == "cli" else None)
|
|
426
|
+
return family if family and family == model_family(session) else None
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def on_session(call, session):
|
|
430
|
+
"""True when this panel call is not independent of the session's model."""
|
|
431
|
+
return bool(skip_reason(call, session))
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def options():
|
|
435
|
+
"""What this machine can run now, the calling host's own CLI last: a second
|
|
436
|
+
opinion from the model already answering is the weakest one on offer."""
|
|
437
|
+
own, found = own_cli(), tp.consult_options()
|
|
438
|
+
return [o for o in found if o != own] + [o for o in found if o == own]
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
def offer_line(word, opt):
|
|
442
|
+
if opt.startswith("cli:"):
|
|
443
|
+
family = tp.CONSULT_CLIS[opt[4:]][2]
|
|
444
|
+
if opt == own_cli():
|
|
445
|
+
family += " (this session's own host)"
|
|
446
|
+
else:
|
|
447
|
+
family = FAMILY[opt]
|
|
448
|
+
return "%s: %s - %s" % (word, opt, family)
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def usable(member):
|
|
452
|
+
"""True when `member` names something this machine can run now."""
|
|
453
|
+
parsed = parse(member)
|
|
454
|
+
return bool(parsed) and option(parsed) in tp.consult_options()
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
def record(members, judge):
|
|
458
|
+
"""Write config.json["consult"]; an unchanged record writes nothing."""
|
|
459
|
+
cfg = tp.config()
|
|
460
|
+
rec = {"members": members}
|
|
461
|
+
if judge:
|
|
462
|
+
rec["judge"] = judge
|
|
463
|
+
if cfg.get("consult") == rec:
|
|
464
|
+
return
|
|
465
|
+
cfg["consult"] = rec
|
|
466
|
+
os.makedirs(os.path.dirname(tp.CONFIG), exist_ok=True)
|
|
467
|
+
tmp = tp.CONFIG + ".tmp"
|
|
468
|
+
with open(tmp, "w", encoding="utf-8") as fh:
|
|
469
|
+
fh.write(json.dumps(cfg, indent=2) + "\n")
|
|
470
|
+
os.replace(tmp, tp.CONFIG)
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def referee_packet(question, answers):
|
|
474
|
+
"""(packet, labels): the answers as "ANSWER 1..n" in a shuffled order, and
|
|
475
|
+
the member each label stands for. No model name reaches the referee and no
|
|
476
|
+
panel order does either, so it cannot favour a name or a position;
|
|
477
|
+
CONSULT_SEED fixes the shuffle so a test can read one order."""
|
|
478
|
+
order = list(answers)
|
|
479
|
+
random.Random(os.environ.get("CONSULT_SEED")).shuffle(order)
|
|
480
|
+
parts = ["QUESTION:\n" + question,
|
|
481
|
+
"The models below answered it independently and did not see each "
|
|
482
|
+
"other's answers.\n"]
|
|
483
|
+
for i, (_label, answer) in enumerate(order, 1):
|
|
484
|
+
parts.append("\n=== ANSWER %d ===\n%s\n" % (i, answer))
|
|
485
|
+
return "\n".join(parts), [label for label, _answer in order]
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
def retry_hint(classes):
|
|
489
|
+
"""The one variable to change, from the failures that actually happened."""
|
|
490
|
+
if not classes:
|
|
491
|
+
return None
|
|
492
|
+
c = classes[0]
|
|
493
|
+
if c == "timeout":
|
|
494
|
+
return "raise --timeout, or drop --online (a slow model is not a broken one)"
|
|
495
|
+
if c == "network":
|
|
496
|
+
return "retry once; if it repeats, check the network or switch --provider"
|
|
497
|
+
if c == "empty":
|
|
498
|
+
return "drop the member that answered empty (refused or filtered)"
|
|
499
|
+
if c == "plain-http":
|
|
500
|
+
return "CONSULT_URL is plain http off this machine; use https (loopback http is allowed)"
|
|
501
|
+
if c == "malformed-reply":
|
|
502
|
+
return "the endpoint is not speaking the chat-completions schema; check CONSULT_URL and --provider"
|
|
503
|
+
if c == "unstructured":
|
|
504
|
+
return "the referee answered outside its five headings; re-run, or judge the panel yourself"
|
|
505
|
+
if c == "credit":
|
|
506
|
+
return ("the provider account has no credit (HTTP 402) - the key is fine; "
|
|
507
|
+
"top it up, or ask the user for another member and record it with --use")
|
|
508
|
+
if c == "key":
|
|
509
|
+
return "set the provider's key, or ask the user for another member and record it with --use"
|
|
510
|
+
if c == "missing":
|
|
511
|
+
return "the agent CLI is not installed; ask the user for another member and record it with --use"
|
|
512
|
+
if c.startswith("cli-exit"):
|
|
513
|
+
return ("the agent CLI failed (not logged in, or a model name it does not "
|
|
514
|
+
"know); run it once by hand, or record another member with --use")
|
|
515
|
+
if c.startswith("http-4"):
|
|
516
|
+
return "the provider rejected the request; a 401/403 means the key, not the question"
|
|
517
|
+
if c.startswith("http-5"):
|
|
518
|
+
return "the provider errored; retry once, then switch --provider"
|
|
519
|
+
return "retry once; if it repeats, switch --provider"
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
VALUE_FLAGS = ("--provider", "--models", "--timeout", "--judge", "--use")
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
def panel(pool, todo, question, timeout, deadline):
|
|
526
|
+
"""Ask every call in parallel and print one section each, in order.
|
|
527
|
+
Returns (answered calls with their answers, failed calls with classes)."""
|
|
528
|
+
futures = [(c, pool.submit(ask_call, c, question, timeout, SYSTEM)) for c in todo]
|
|
529
|
+
answers, failures = [], []
|
|
530
|
+
for call, fut in futures:
|
|
531
|
+
try:
|
|
532
|
+
label, answer, err, cls = fut.result(timeout=deadline)
|
|
533
|
+
except concurrent.futures.TimeoutError:
|
|
534
|
+
label, answer, err, cls = call[0], None, \
|
|
535
|
+
"timed out after %ss (raise --timeout, or drop --online)" % deadline, "timeout"
|
|
536
|
+
print("## %s" % label)
|
|
537
|
+
if err:
|
|
538
|
+
print("ERROR: %s" % err)
|
|
539
|
+
failures.append((call, cls))
|
|
540
|
+
else:
|
|
541
|
+
print(answer)
|
|
542
|
+
answers.append((call, answer))
|
|
543
|
+
print()
|
|
544
|
+
return answers, failures
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
def main():
|
|
548
|
+
args = sys.argv[1:]
|
|
549
|
+
# Before the loop: an unrecognized flag falls through to the `question is
|
|
550
|
+
# None` arm below and the flag text becomes the prompt, so `--help` used to
|
|
551
|
+
# spend a paid panel call and print no usage.
|
|
552
|
+
if "-h" in args or "--help" in args:
|
|
553
|
+
print(__doc__.strip())
|
|
554
|
+
return 0
|
|
555
|
+
provider, models, timeout, question, judge = None, None, 120, None, None
|
|
556
|
+
online, referee, use = False, True, None
|
|
557
|
+
i = 0
|
|
558
|
+
while i < len(args):
|
|
559
|
+
if args[i] in VALUE_FLAGS and i + 1 >= len(args):
|
|
560
|
+
# Without this, a trailing flag fails to match its arm, falls to the
|
|
561
|
+
# `question is None` branch below, and the flag text becomes the
|
|
562
|
+
# prompt - a paid request for a typo, answered with exit 3 instead
|
|
563
|
+
# of the documented misuse exit 1.
|
|
564
|
+
sys.exit("consult: %s needs a value" % args[i])
|
|
565
|
+
if args[i] == "--provider" and i + 1 < len(args):
|
|
566
|
+
provider = args[i + 1]
|
|
567
|
+
i += 2
|
|
568
|
+
elif args[i] == "--models" and i + 1 < len(args):
|
|
569
|
+
models = args[i + 1].split(",")
|
|
570
|
+
i += 2
|
|
571
|
+
elif args[i] == "--judge" and i + 1 < len(args):
|
|
572
|
+
judge = args[i + 1]
|
|
573
|
+
i += 2
|
|
574
|
+
elif args[i] == "--use" and i + 1 < len(args):
|
|
575
|
+
use = [m.strip() for m in args[i + 1].split(",") if m.strip()]
|
|
576
|
+
i += 2
|
|
577
|
+
elif args[i] == "--online":
|
|
578
|
+
online = True
|
|
579
|
+
i += 1
|
|
580
|
+
elif args[i] == "--no-referee":
|
|
581
|
+
referee = False
|
|
582
|
+
i += 1
|
|
583
|
+
elif args[i] == "--timeout" and i + 1 < len(args):
|
|
584
|
+
timeout = int(args[i + 1])
|
|
585
|
+
i += 2
|
|
586
|
+
elif question is None:
|
|
587
|
+
question = args[i]
|
|
588
|
+
i += 1
|
|
589
|
+
else:
|
|
590
|
+
sys.exit("consult: unexpected argument %r" % args[i])
|
|
591
|
+
if provider is not None and provider not in PROVIDERS:
|
|
592
|
+
sys.exit("consult: unknown provider %r (use %s)"
|
|
593
|
+
% (provider, " or ".join(sorted(PROVIDERS))))
|
|
594
|
+
if online and provider not in (None, "openrouter"):
|
|
595
|
+
# ":online" is an OpenRouter suffix; appending it elsewhere yields an
|
|
596
|
+
# unknown model id, so refuse rather than fail every model.
|
|
597
|
+
sys.exit("consult: --online is OpenRouter-only; drop it or use "
|
|
598
|
+
"--provider openrouter")
|
|
599
|
+
if use is not None:
|
|
600
|
+
# Recording asks no model. A member that cannot run here is refused so
|
|
601
|
+
# a typo never becomes the choice every later run fails on.
|
|
602
|
+
bad = [m for m in use + ([judge] if judge else []) if not usable(m)]
|
|
603
|
+
if not use or bad:
|
|
604
|
+
sys.exit("consult: cannot record %s: not available here (available: %s)"
|
|
605
|
+
% (", ".join(bad) or "an empty list",
|
|
606
|
+
", ".join(options()) or "none"))
|
|
607
|
+
record(use, judge)
|
|
608
|
+
print("consult: recorded members %s%s" % (
|
|
609
|
+
",".join(use), "; referee %s" % judge if judge else ""))
|
|
610
|
+
if question is None:
|
|
611
|
+
return 0
|
|
612
|
+
# A flag that names a provider or its models is a one-run HTTP panel, the
|
|
613
|
+
# contract every existing caller and script was written against.
|
|
614
|
+
explicit = bool(provider or models or online or os.environ.get("CONSULT_MODELS"))
|
|
615
|
+
provider = provider or "openrouter"
|
|
616
|
+
if question == "-":
|
|
617
|
+
# A packet is often several KB, and argv has a length limit (and some
|
|
618
|
+
# endpoints stall before the request starts on a long argument), so a
|
|
619
|
+
# redirected packet is read from stdin instead. Deliberately explicit:
|
|
620
|
+
# reading stdin for a missing argument would turn a stray open pipe
|
|
621
|
+
# into a paid prompt where the caller expected the usage error.
|
|
622
|
+
question = "" if sys.stdin.isatty() else sys.stdin.read().strip()
|
|
623
|
+
if not question:
|
|
624
|
+
sys.exit(__doc__.strip())
|
|
625
|
+
|
|
626
|
+
recorded = (tp.config().get("consult") or {}) if not explicit else {}
|
|
627
|
+
if explicit:
|
|
628
|
+
if online:
|
|
629
|
+
models = [m if m.strip().endswith(":online") else m.strip() + ":online"
|
|
630
|
+
for m in (models or defaults(provider))]
|
|
631
|
+
if not key_for(provider):
|
|
632
|
+
print("consult: " + no_key(provider), file=sys.stderr)
|
|
633
|
+
sys.exit(2)
|
|
634
|
+
members = ["%s:%s" % (provider, m.strip()) for m in (models or defaults(provider))]
|
|
635
|
+
else:
|
|
636
|
+
members = [m for m in recorded.get("members") or [] if parse(m)]
|
|
637
|
+
if not members:
|
|
638
|
+
found = options()
|
|
639
|
+
if not found:
|
|
640
|
+
print("consult: no member available - install an agent CLI (%s) "
|
|
641
|
+
"or set a provider key (%s)" % (
|
|
642
|
+
", ".join(tp.CONSULT_CLIS), ", ".join(PROVIDERS)),
|
|
643
|
+
file=sys.stderr)
|
|
644
|
+
sys.exit(2)
|
|
645
|
+
print("consult: no recorded choice, so nothing was asked.")
|
|
646
|
+
for opt in found:
|
|
647
|
+
print(offer_line("offer", opt))
|
|
648
|
+
print("next: ask the user which of these to use (two or more make a "
|
|
649
|
+
"panel), then run: consult --use <member>[,<member>] and ask again")
|
|
650
|
+
sys.stdout.flush()
|
|
651
|
+
os._exit(4)
|
|
652
|
+
|
|
653
|
+
session = session_model()
|
|
654
|
+
# A member THIS run names with --use is the user overriding the skip for it;
|
|
655
|
+
# the record alone is not, which is the whole point of the skip. An explicit
|
|
656
|
+
# --provider/--models run names its own models too, so it is never skipped.
|
|
657
|
+
named = {option(p) for p in (parse(m) for m in (use or [])) if p}
|
|
658
|
+
todo, skipped, kept, unknown = [], [], [], []
|
|
659
|
+
skip_opts = set()
|
|
660
|
+
for call in (c for m in members for c in calls(m)):
|
|
661
|
+
reason = None if explicit else skip_reason(call, session)
|
|
662
|
+
why = ("runs on this session's own model" if reason == "model" else
|
|
663
|
+
"same model family as this session's: %s" % reason)
|
|
664
|
+
if reason:
|
|
665
|
+
if call[1] in named:
|
|
666
|
+
kept.append((call[0], why))
|
|
667
|
+
else:
|
|
668
|
+
skipped.append((call[0], why))
|
|
669
|
+
skip_opts.add(call[1])
|
|
670
|
+
continue
|
|
671
|
+
elif session and call[2] == "cli" and not call[4]:
|
|
672
|
+
# A bare `cli:omp` answers with whatever it is configured for, and
|
|
673
|
+
# that is not readable here - unknown, never read as the session's.
|
|
674
|
+
unknown.append(call[0])
|
|
675
|
+
todo.append(call)
|
|
676
|
+
if session:
|
|
677
|
+
print("consult: this session's model is %s" % session)
|
|
678
|
+
for label, why in skipped:
|
|
679
|
+
print("skipped: %s (%s)" % (label, why))
|
|
680
|
+
for label in unknown:
|
|
681
|
+
print("unknown: %s (a CLI whose model cannot be read, so it is not "
|
|
682
|
+
"excluded)" % label)
|
|
683
|
+
for label, why in kept:
|
|
684
|
+
print("asked anyway: %s (%s, kept because --use named it)" % (label, why))
|
|
685
|
+
else:
|
|
686
|
+
print("consult: no session model could be read (CONSULT_SESSION_MODEL, or "
|
|
687
|
+
"omp's modelRoles.default), so no member was skipped")
|
|
688
|
+
deadline = timeout + 15 # wall-clock cap per call: urllib's socket timeout resets on slow :online streams
|
|
689
|
+
pool = concurrent.futures.ThreadPoolExecutor(max(len(todo), 2))
|
|
690
|
+
answers, failures = panel(pool, todo, question, timeout, deadline)
|
|
691
|
+
fallback = None
|
|
692
|
+
spare = [c for c in calls("openrouter") if not on_session(c, session)]
|
|
693
|
+
if not answers and not explicit and "openrouter" not in {c[1] for c in todo} \
|
|
694
|
+
and key_for("openrouter") and spare:
|
|
695
|
+
# The owner's order: the recorded members first, OpenRouter only when
|
|
696
|
+
# every one of them failed, and never a second time once it has.
|
|
697
|
+
fallback = "openrouter"
|
|
698
|
+
print("## fallback: every recorded member failed, asking OpenRouter's default panel\n")
|
|
699
|
+
more, lost = panel(pool, spare, question, timeout, deadline)
|
|
700
|
+
answers += more
|
|
701
|
+
failures += lost
|
|
702
|
+
|
|
703
|
+
# Stage two, and it is deliberately ONE sequential call: the panel above is
|
|
704
|
+
# already independent and parallel, so what is left to buy is the
|
|
705
|
+
# cross-examination, not more of the same opinion. A full multi-round debate
|
|
706
|
+
# (revise, re-critique, blind judge) was measured as ceremony here: the
|
|
707
|
+
# client is stateless, the cost triples and the caller wants one answer.
|
|
708
|
+
classes = [cls for _c, cls in failures]
|
|
709
|
+
failed = ["%s (%s)" % (c[0], cls) for c, cls in failures]
|
|
710
|
+
judged = verdict = jcls = None
|
|
711
|
+
if referee and answers:
|
|
712
|
+
name = judge or os.environ.get("CONSULT_JUDGE", "").strip() or recorded.get("judge")
|
|
713
|
+
jcall = None
|
|
714
|
+
if not name and not explicit:
|
|
715
|
+
# No referee chosen: an available member that is not on the panel,
|
|
716
|
+
# so the referee never judges a packet holding its own answer. An
|
|
717
|
+
# explicit --provider/--models run stays on its one provider.
|
|
718
|
+
in_panel = {c[1] for c in todo} | {c[1] for c, _a in answers}
|
|
719
|
+
spare = next((o for o in options() if o not in in_panel
|
|
720
|
+
and not on_session(calls(o)[0], session)), None)
|
|
721
|
+
jcall = calls(spare)[0] if spare else None
|
|
722
|
+
if jcall is None and not name:
|
|
723
|
+
# Never the session's own model when another member answered: a
|
|
724
|
+
# referee that is the panel's weakest opinion judges its own kind.
|
|
725
|
+
jcall = next((c for c, _a in answers if not on_session(c, session)),
|
|
726
|
+
answers[0][0])
|
|
727
|
+
print("note: the referee %s is a panel member, so it judges a packet "
|
|
728
|
+
"holding its own answer; record an outside referee with "
|
|
729
|
+
"consult --use <members> --judge <member>\n" % jcall[0])
|
|
730
|
+
elif parse(name or ""):
|
|
731
|
+
jcall = calls(name)[0]
|
|
732
|
+
elif name: # a bare model id: the run's own HTTP provider answers it
|
|
733
|
+
jcall = (name, provider, "http", provider, name)
|
|
734
|
+
packet, labels = referee_packet(question, [(c[0], a) for c, a in answers])
|
|
735
|
+
fut = pool.submit(ask_call, jcall, packet, timeout, REFEREE)
|
|
736
|
+
try:
|
|
737
|
+
judged, verdict, jerr, jcls = fut.result(timeout=deadline)
|
|
738
|
+
except concurrent.futures.TimeoutError:
|
|
739
|
+
judged, verdict, jerr, jcls = jcall[0], None, "timed out after %ss" % deadline, "timeout"
|
|
740
|
+
print("## referee (%s)" % judged)
|
|
741
|
+
print("answer labels: %s" % ", ".join(
|
|
742
|
+
"Answer %d = %s" % (i, label) for i, label in enumerate(labels, 1)))
|
|
743
|
+
if not jerr:
|
|
744
|
+
missing = [f for f in REFEREE_FIELDS if f not in verdict.lower()]
|
|
745
|
+
if missing:
|
|
746
|
+
# A reply that skipped a heading is not a judgement under the
|
|
747
|
+
# fields the caller reads back, so it takes the same road as a
|
|
748
|
+
# referee that never answered: the panel stands unjudged. The
|
|
749
|
+
# text is not printed - an unheadlined paragraph in the referee
|
|
750
|
+
# section reads as a verdict whatever the footer says.
|
|
751
|
+
jerr = "the reply did not answer: %s" % ", ".join(missing)
|
|
752
|
+
jcls, verdict = "unstructured", None
|
|
753
|
+
if jerr:
|
|
754
|
+
print("ERROR: %s" % jerr)
|
|
755
|
+
classes.append(jcls)
|
|
756
|
+
failed.append("referee (%s)" % jcls)
|
|
757
|
+
else:
|
|
758
|
+
print(verdict)
|
|
759
|
+
print()
|
|
760
|
+
|
|
761
|
+
print("consulted: %s | failed: %s" % (
|
|
762
|
+
", ".join(c[0] for c, _a in answers) or "none", ", ".join(failed) or "none"))
|
|
763
|
+
if fallback:
|
|
764
|
+
print("fallback: %s" % fallback)
|
|
765
|
+
if judged:
|
|
766
|
+
# A dead referee is a degraded arena, not a silent one: the panel
|
|
767
|
+
# answers are unjudged and the caller has to be told.
|
|
768
|
+
if verdict is None:
|
|
769
|
+
print("referee: FAILED (%s) -- no cross-examination, the panel "
|
|
770
|
+
"answers stand unjudged" % jcls)
|
|
771
|
+
else:
|
|
772
|
+
print("referee: %s" % judged)
|
|
773
|
+
hint = retry_hint(classes)
|
|
774
|
+
if hint:
|
|
775
|
+
print("retry: %s" % hint)
|
|
776
|
+
code = 0 if answers else 3
|
|
777
|
+
if not answers and not explicit:
|
|
778
|
+
# The recorded choice is the user's, so consult never swaps it on its
|
|
779
|
+
# own: it names what is left and the agent asks again.
|
|
780
|
+
lost = {c[1] for c, _cls in failures} | skip_opts
|
|
781
|
+
left = [o for o in options() if o not in lost]
|
|
782
|
+
for opt in left:
|
|
783
|
+
print(offer_line("reoffer", opt))
|
|
784
|
+
print("next: ask the user which of these to use instead, then run: "
|
|
785
|
+
"consult --use <member>[,<member>] and ask again" if left else
|
|
786
|
+
"next: nothing else can answer here; tell the user the second "
|
|
787
|
+
"opinion was skipped (install an agent CLI or add a provider key)")
|
|
788
|
+
code = 5
|
|
789
|
+
pool.shutdown(wait=False)
|
|
790
|
+
sys.stdout.flush()
|
|
791
|
+
# ponytail: hard exit so a still-hung request thread cannot wedge the process (18-min hang seen);
|
|
792
|
+
# flush above preserves output. Upgrade to per-thread cancellation only if a leaked socket ever matters.
|
|
793
|
+
os._exit(code)
|
|
794
|
+
|
|
795
|
+
|
|
796
|
+
if __name__ == "__main__":
|
|
797
|
+
main()
|