clawed 2.3.8__tar.gz → 2.3.9__tar.gz
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.
- {clawed-2.3.8 → clawed-2.3.9}/PKG-INFO +46 -9
- {clawed-2.3.8 → clawed-2.3.9}/README.md +34 -4
- {clawed-2.3.8 → clawed-2.3.9}/clawed/__init__.py +1 -1
- {clawed-2.3.8 → clawed-2.3.9}/clawed/_legacy_gateway.py +1 -1
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent.py +24 -5
- {clawed-2.3.8 → clawed-2.3.9}/clawed/cli.py +2 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/generate.py +44 -9
- clawed-2.3.9/clawed/commands/train.py +258 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/config.py +34 -6
- {clawed-2.3.8 → clawed-2.3.9}/clawed/generation.py +199 -27
- {clawed-2.3.8 → clawed-2.3.9}/clawed/handlers/generate.py +1 -1
- clawed-2.3.8/clawed/openclaw_plugin.py → clawed-2.3.9/clawed/hermes_plugin.py +1 -1
- {clawed-2.3.8 → clawed-2.3.9}/clawed/lesson.py +14 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/llm.py +2 -5
- {clawed-2.3.8 → clawed-2.3.9}/clawed/mcp_server.py +1 -1
- clawed-2.3.9/clawed/multi_agent.py +302 -0
- clawed-2.3.9/clawed/openclaw_plugin.py +24 -0
- clawed-2.3.9/clawed/persona.py +166 -0
- clawed-2.3.9/clawed/prompts/multi_agent_researcher.txt +69 -0
- clawed-2.3.9/clawed/prompts/multi_agent_reviewer.txt +58 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/scheduler.py +1 -1
- clawed-2.3.8/clawed/transports/openclaw.py → clawed-2.3.9/clawed/transports/hermes.py +15 -15
- clawed-2.3.9/clawed/transports/openclaw.py +11 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/workspace.py +1 -1
- {clawed-2.3.8 → clawed-2.3.9}/pyproject.toml +18 -5
- clawed-2.3.8/clawed/persona.py +0 -85
- {clawed-2.3.8 → clawed-2.3.9}/.gitignore +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/LICENSE +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/__main__.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/__init__.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/approvals.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/autonomy.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/context.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/core.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/custom_tools.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/drive/__init__.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/drive/auth.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/drive/client.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/fake_llm.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/loop.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/memory/__init__.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/memory/curriculum.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/memory/curriculum_kb.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/memory/embeddings.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/memory/episodes.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/memory/identity.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/memory/loader.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/memory/preferences.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/planner.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/prompt.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/scheduler.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/__init__.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/base.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/configure_profile.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/curriculum_map.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/drive_create_doc.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/drive_create_slides.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/drive_list.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/drive_organize.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/drive_read.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/drive_upload.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/export_document.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/gap_analysis.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/generate_assessment.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/generate_lesson.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/generate_lesson_bundle.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/generate_materials.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/generate_unit.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/ingest_materials.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/parent_comm.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/read_heartbeat.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/read_workspace.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/request_approval.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/schedule_task.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/search_lessons.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/search_my_materials.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/search_standards.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/student_insights.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/sub_packet.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/switch_model.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/update_soul.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/analytics.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/__init__.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/deps.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/routes/__init__.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/routes/chat.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/routes/export.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/routes/feedback.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/routes/gateway_chat.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/routes/generate.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/routes/ingest.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/routes/lessons.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/routes/school.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/routes/settings.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/routes/tools.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/server.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/static/app.js +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/static/style.css +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/static/widget.js +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/templates/analytics.html +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/templates/base.html +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/templates/dashboard.html +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/templates/generate.html +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/templates/index.html +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/templates/lesson.html +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/templates/profile.html +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/templates/settings.html +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/templates/stats.html +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/api/templates/students.html +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/assessment.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/asset_registry.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/async_utils.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/auth/__init__.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/auth/google_auth.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/bot_state.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/chat.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/cli_chat.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/__init__.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/_helpers.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/bot.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/config.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/config_llm.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/config_profile.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/export.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/generate_assessment.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/generate_unit.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/queue.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/schedule_cmd.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/sub.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/workspace_cmd.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/compile_slides.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/compile_student.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/compile_teacher.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/corpus.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/curriculum_map.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/database.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/__init__.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/demo_assessment.json +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/demo_formative_assessment.json +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/demo_lesson_materials.json +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/demo_lesson_science_g6.json +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/demo_lesson_social_studies_g8.json +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/demo_master_content.json +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/demo_pacing_guide.json +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/demo_quiz.json +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/demo_rubric.json +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/demo_unit_plan.json +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/demo_year_map.json +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/differentiation.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/doc_export.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/drive.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/evaluation.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/export_docx.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/export_handout.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/export_markdown.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/export_pdf.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/export_pptx.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/export_templates.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/export_theme.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/exporter.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/failure_codes.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/feedback.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/formats/__init__.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/formats/flipchart.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/formats/notebook.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/formats/xbk.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/gateway.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/gateway_response.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/generation_report.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/handlers/__init__.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/handlers/export.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/handlers/feedback.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/handlers/gaps.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/handlers/ingest.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/handlers/misc.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/handlers/onboard.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/handlers/schedule.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/handlers/standards.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/image_pipeline.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/improver.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/ingestor.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/io.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/master_content.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/materials.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/memory_engine.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/model_router.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/models.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/onboarding.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/parent_comm.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/persona_evolution.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/planner.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/504_accommodations.txt +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/admin_lesson_plan.txt +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/assessment.txt +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/curriculum_gaps.txt +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/dbq_assessment.txt +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/differentiation.txt +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/formative_assessment.txt +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/iep_modification.txt +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/lesson_plan.txt +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/master_content.txt +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/pacing_guide.txt +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/parent_note.txt +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/persona_extract.txt +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/quiz.txt +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/rubric.txt +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/student_packet.txt +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/sub_packet.txt +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/summative_assessment.txt +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/tiered_assignments.txt +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/unit_plan.txt +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/worksheet.txt +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/year_map.txt +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/quality.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/reading_report.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/router.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/sanitize.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/school.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/search.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/__init__.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/art.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/base.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/computer_science.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/ela.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/foreign_language.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/history.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/library.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/math.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/music.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/physical_education.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/science.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/social_studies.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/special_education.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/slide_images.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/standards.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/state.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/state_standards.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/student_bot.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/student_cli.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/student_telegram_bot.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/sub_packet.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/task_queue.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/templates_lib.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/tools.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/transports/__init__.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/transports/cli.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/transports/student_telegram.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/transports/telegram.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/transports/web.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/tui.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/tui_chat.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/validation.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/voice.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/clawed/voice_check.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/eduagent/__init__.py +0 -0
- {clawed-2.3.8 → clawed-2.3.9}/eduagent/_compat.py +0 -0
|
@@ -1,22 +1,29 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: clawed
|
|
3
|
-
Version: 2.3.
|
|
4
|
-
Summary:
|
|
5
|
-
Project-URL: Homepage, https://github.
|
|
3
|
+
Version: 2.3.9
|
|
4
|
+
Summary: Free AI lesson planner for teachers. Learns your teaching voice from your curriculum files and generates complete lesson packages. Open source, runs locally, no subscription.
|
|
5
|
+
Project-URL: Homepage, https://sirhanmacx.github.io/Claw-ED
|
|
6
6
|
Project-URL: Documentation, https://github.com/SirhanMacx/Claw-ED#readme
|
|
7
|
-
Project-URL:
|
|
7
|
+
Project-URL: Repository, https://github.com/SirhanMacx/Claw-ED
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/SirhanMacx/Claw-ED/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/SirhanMacx/Claw-ED/blob/main/CHANGELOG.md
|
|
10
|
+
Project-URL: PyPI, https://pypi.org/project/clawed/
|
|
8
11
|
Author: Jon Maccarello & Claw-ED contributors
|
|
9
12
|
License-Expression: MIT
|
|
10
13
|
License-File: LICENSE
|
|
11
|
-
Keywords: ai,claw-ed,clawed,education,lesson-plans,teachers,teaching
|
|
14
|
+
Keywords: ai,ai-for-teachers,claw-ed,clawed,curriculum,edtech,education,hermes-agent,k12,lesson-plan-generator,lesson-planner,lesson-planning,lesson-plans,open-source-education,pedagogy,teacher-tools,teachers,teaching,teaching-assistant
|
|
12
15
|
Classifier: Development Status :: 3 - Alpha
|
|
16
|
+
Classifier: Environment :: Console
|
|
13
17
|
Classifier: Intended Audience :: Education
|
|
14
18
|
Classifier: License :: OSI Approved :: MIT License
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
15
20
|
Classifier: Programming Language :: Python :: 3
|
|
16
21
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
22
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
23
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
24
|
Classifier: Topic :: Education
|
|
25
|
+
Classifier: Topic :: Education :: Computer Aided Instruction (CAI)
|
|
26
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
27
|
Requires-Python: >=3.10
|
|
21
28
|
Requires-Dist: apscheduler<4.0,>=3.10.0
|
|
22
29
|
Requires-Dist: fastapi<1.0,>=0.110.0
|
|
@@ -71,17 +78,47 @@ Description-Content-Type: text/markdown
|
|
|
71
78
|
|
|
72
79
|
Your personal AI teaching agent. Runs on your machine, learns your voice, works while you sleep.
|
|
73
80
|
|
|
74
|
-
Built on the
|
|
81
|
+
Built on the Hermes Agent framework (NousResearch). Open source. MIT license.
|
|
75
82
|
|
|
76
83
|
<p align="center">
|
|
77
|
-
<a href="https://pypi.org/project/clawed/"><img src="https://img.shields.io/pypi/v/clawed?color=blue" alt="PyPI"></a>
|
|
78
|
-
<a href="https://
|
|
84
|
+
<a href="https://pypi.org/project/clawed/"><img src="https://img.shields.io/pypi/v/clawed?color=blue" alt="PyPI version"></a>
|
|
85
|
+
<a href="https://pypi.org/project/clawed/"><img src="https://img.shields.io/pypi/pyversions/clawed" alt="Python versions"></a>
|
|
79
86
|
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-green" alt="MIT License"></a>
|
|
80
87
|
<a href="https://github.com/SirhanMacx/Claw-ED/stargazers"><img src="https://img.shields.io/github/stars/SirhanMacx/Claw-ED" alt="GitHub stars"></a>
|
|
88
|
+
<a href="https://pepy.tech/project/clawed"><img src="https://static.pepy.tech/badge/clawed" alt="Downloads"></a>
|
|
81
89
|
</p>
|
|
82
90
|
|
|
83
91
|
---
|
|
84
92
|
|
|
93
|
+
## What's new in v2.3.9
|
|
94
|
+
|
|
95
|
+
**Multi-agent lesson generation.** Instead of one LLM call doing everything, `--multi-agent` spawns a team of three specialized agents: a Researcher who finds primary sources and historical context, a Writer who drafts the lesson in your voice, and a Reviewer who scores quality on voice fidelity, pedagogy, and differentiation — sending it back for revision if any dimension scores below 7/10. The result: higher-quality lessons with better sources and stronger voice matching.
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
clawed lesson "The Missouri Compromise" -g 8 --multi-agent
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Claude Code integration.** Claw-ED now auto-reads OAuth tokens from Claude Code's credential store (`~/.claude/.credentials.json`). If you have Claude Code installed, Claw-ED just works — no separate API key setup needed, and tokens auto-refresh. Claw-ED also works as an MCP server that Claude Code can use directly:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
clawed mcp-server # Add to Claude Code as an MCP tool
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Continuous improvement pipeline.** The new `clawed train` command lets your AI fleet (or you) continuously improve lesson quality:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
clawed train --drive # Ingest new files from Google Drive, refine persona
|
|
111
|
+
clawed train --path ./files # Ingest local curriculum files
|
|
112
|
+
clawed train --benchmark -n 5 # Generate 5 lessons and score them
|
|
113
|
+
clawed train --full # Drive ingest + benchmark
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Persona refinement is now incremental — new voice markers merge with existing ones instead of overwriting. Your teaching identity gets richer over time.
|
|
117
|
+
|
|
118
|
+
**OAuth auth fix.** All Anthropic API calls now correctly detect OAuth tokens and use Bearer authentication with Claude Code identity headers. Fixes the 401 errors that affected fleet agents and Claw-ED direct usage.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
85
122
|
## What's new in v2.3.8
|
|
86
123
|
|
|
87
124
|
**No more silent failures.** Every step of lesson generation now reports what happened — persona loading, material search, quality review, voice matching. If something fails, you'll know exactly what and why, with structured NLAH failure codes. Quality review runs automatically on every generated lesson and fails closed: if the review itself crashes, it reports failure instead of silently passing.
|
|
@@ -116,7 +153,7 @@ Built on the OpenClaw agent framework. Open source. MIT license.
|
|
|
116
153
|
|
|
117
154
|
## What is this?
|
|
118
155
|
|
|
119
|
-
Claw-ED is a personal AI agent for teachers. It uses the same architecture as
|
|
156
|
+
Claw-ED is a personal AI agent for teachers. It uses the same architecture as Hermes Agent -- SOUL.md for identity, layered memory, a workspace, cron jobs, tool use, and autonomous operation. The difference is what it knows: your lesson plans, your teaching style, your standards, your students.
|
|
120
157
|
|
|
121
158
|
You feed it your curriculum files and it learns your voice. Not a generic "teacher tone" -- your actual patterns, your vocabulary, the way you structure a Do Now or frame an essential question. Then it works: drafting lessons that sound like you wrote them, organizing your Google Drive, answering student questions in your voice, prepping your week before you wake up.
|
|
122
159
|
|
|
@@ -2,17 +2,47 @@
|
|
|
2
2
|
|
|
3
3
|
Your personal AI teaching agent. Runs on your machine, learns your voice, works while you sleep.
|
|
4
4
|
|
|
5
|
-
Built on the
|
|
5
|
+
Built on the Hermes Agent framework (NousResearch). Open source. MIT license.
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<a href="https://pypi.org/project/clawed/"><img src="https://img.shields.io/pypi/v/clawed?color=blue" alt="PyPI"></a>
|
|
9
|
-
<a href="https://
|
|
8
|
+
<a href="https://pypi.org/project/clawed/"><img src="https://img.shields.io/pypi/v/clawed?color=blue" alt="PyPI version"></a>
|
|
9
|
+
<a href="https://pypi.org/project/clawed/"><img src="https://img.shields.io/pypi/pyversions/clawed" alt="Python versions"></a>
|
|
10
10
|
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-green" alt="MIT License"></a>
|
|
11
11
|
<a href="https://github.com/SirhanMacx/Claw-ED/stargazers"><img src="https://img.shields.io/github/stars/SirhanMacx/Claw-ED" alt="GitHub stars"></a>
|
|
12
|
+
<a href="https://pepy.tech/project/clawed"><img src="https://static.pepy.tech/badge/clawed" alt="Downloads"></a>
|
|
12
13
|
</p>
|
|
13
14
|
|
|
14
15
|
---
|
|
15
16
|
|
|
17
|
+
## What's new in v2.3.9
|
|
18
|
+
|
|
19
|
+
**Multi-agent lesson generation.** Instead of one LLM call doing everything, `--multi-agent` spawns a team of three specialized agents: a Researcher who finds primary sources and historical context, a Writer who drafts the lesson in your voice, and a Reviewer who scores quality on voice fidelity, pedagogy, and differentiation — sending it back for revision if any dimension scores below 7/10. The result: higher-quality lessons with better sources and stronger voice matching.
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
clawed lesson "The Missouri Compromise" -g 8 --multi-agent
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Claude Code integration.** Claw-ED now auto-reads OAuth tokens from Claude Code's credential store (`~/.claude/.credentials.json`). If you have Claude Code installed, Claw-ED just works — no separate API key setup needed, and tokens auto-refresh. Claw-ED also works as an MCP server that Claude Code can use directly:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
clawed mcp-server # Add to Claude Code as an MCP tool
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Continuous improvement pipeline.** The new `clawed train` command lets your AI fleet (or you) continuously improve lesson quality:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
clawed train --drive # Ingest new files from Google Drive, refine persona
|
|
35
|
+
clawed train --path ./files # Ingest local curriculum files
|
|
36
|
+
clawed train --benchmark -n 5 # Generate 5 lessons and score them
|
|
37
|
+
clawed train --full # Drive ingest + benchmark
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Persona refinement is now incremental — new voice markers merge with existing ones instead of overwriting. Your teaching identity gets richer over time.
|
|
41
|
+
|
|
42
|
+
**OAuth auth fix.** All Anthropic API calls now correctly detect OAuth tokens and use Bearer authentication with Claude Code identity headers. Fixes the 401 errors that affected fleet agents and Claw-ED direct usage.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
16
46
|
## What's new in v2.3.8
|
|
17
47
|
|
|
18
48
|
**No more silent failures.** Every step of lesson generation now reports what happened — persona loading, material search, quality review, voice matching. If something fails, you'll know exactly what and why, with structured NLAH failure codes. Quality review runs automatically on every generated lesson and fails closed: if the review itself crashes, it reports failure instead of silently passing.
|
|
@@ -47,7 +77,7 @@ Built on the OpenClaw agent framework. Open source. MIT license.
|
|
|
47
77
|
|
|
48
78
|
## What is this?
|
|
49
79
|
|
|
50
|
-
Claw-ED is a personal AI agent for teachers. It uses the same architecture as
|
|
80
|
+
Claw-ED is a personal AI agent for teachers. It uses the same architecture as Hermes Agent -- SOUL.md for identity, layered memory, a workspace, cron jobs, tool use, and autonomous operation. The difference is what it knows: your lesson plans, your teaching style, your standards, your students.
|
|
51
81
|
|
|
52
82
|
You feed it your curriculum files and it learns your voice. Not a generic "teacher tone" -- your actual patterns, your vocabulary, the way you structure a Do Now or frame an essential question. Then it works: drafting lessons that sound like you wrote them, organizing your Google Drive, answering student questions in your voice, prepping your week before you wake up.
|
|
53
83
|
|
|
@@ -17,7 +17,7 @@ if hasattr(sys.stderr, "reconfigure"):
|
|
|
17
17
|
except Exception:
|
|
18
18
|
pass
|
|
19
19
|
|
|
20
|
-
__version__ = "2.3.
|
|
20
|
+
__version__ = "2.3.9"
|
|
21
21
|
__author__ = "Jon Maccarello & Claw-ED contributors"
|
|
22
22
|
__description__ = "Personal AI teaching agent. Learns your voice, works while you sleep."
|
|
23
23
|
|
|
@@ -350,7 +350,7 @@ class Gateway:
|
|
|
350
350
|
|
|
351
351
|
async def _status_response(self, teacher_id: str) -> GatewayResponse:
|
|
352
352
|
try:
|
|
353
|
-
from clawed.
|
|
353
|
+
from clawed.hermes_plugin import _show_status
|
|
354
354
|
from clawed.state import TeacherSession
|
|
355
355
|
session = TeacherSession.load(teacher_id)
|
|
356
356
|
return GatewayResponse(text=_show_status(session))
|
|
@@ -13,6 +13,29 @@ from __future__ import annotations
|
|
|
13
13
|
|
|
14
14
|
import json
|
|
15
15
|
import logging
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _anthropic_headers(api_key: str) -> dict[str, str]:
|
|
19
|
+
"""Build Anthropic API headers, auto-detecting OAuth vs regular API keys.
|
|
20
|
+
|
|
21
|
+
OAuth tokens (sk-ant-oat01-*) need Bearer auth + Claude Code identity
|
|
22
|
+
headers. Regular API keys (sk-ant-api*) use x-api-key.
|
|
23
|
+
"""
|
|
24
|
+
is_oauth = api_key.startswith("sk-ant-") and not api_key.startswith("sk-ant-api")
|
|
25
|
+
if is_oauth:
|
|
26
|
+
return {
|
|
27
|
+
"authorization": f"Bearer {api_key}",
|
|
28
|
+
"anthropic-version": "2023-06-01",
|
|
29
|
+
"anthropic-beta": "interleaved-thinking-2025-05-14,claude-code-20250219,oauth-2025-04-20",
|
|
30
|
+
"user-agent": "claude-cli/1.0.0 (external, cli)",
|
|
31
|
+
"x-app": "cli",
|
|
32
|
+
"content-type": "application/json",
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
"x-api-key": api_key,
|
|
36
|
+
"anthropic-version": "2023-06-01",
|
|
37
|
+
"content-type": "application/json",
|
|
38
|
+
}
|
|
16
39
|
from typing import Any
|
|
17
40
|
|
|
18
41
|
from clawed.models import AppConfig, LLMProvider
|
|
@@ -137,11 +160,7 @@ async def _anthropic_with_tools(
|
|
|
137
160
|
async with httpx.AsyncClient(timeout=120.0) as client:
|
|
138
161
|
resp = await client.post(
|
|
139
162
|
"https://api.anthropic.com/v1/messages",
|
|
140
|
-
headers=
|
|
141
|
-
"x-api-key": api_key,
|
|
142
|
-
"anthropic-version": "2023-06-01",
|
|
143
|
-
"content-type": "application/json",
|
|
144
|
-
},
|
|
163
|
+
headers=_anthropic_headers(api_key),
|
|
145
164
|
json={
|
|
146
165
|
"model": config.anthropic_model,
|
|
147
166
|
"max_tokens": 4096,
|
|
@@ -39,6 +39,7 @@ from clawed.commands.generate import generate_app
|
|
|
39
39
|
from clawed.commands.queue import queue_app
|
|
40
40
|
from clawed.commands.schedule_cmd import schedule_app
|
|
41
41
|
from clawed.commands.sub import sub_app
|
|
42
|
+
from clawed.commands.train import train_app
|
|
42
43
|
from clawed.commands.workspace_cmd import workspace_app
|
|
43
44
|
|
|
44
45
|
# ── Build the main app ──────────────────────────────────────────────────
|
|
@@ -241,6 +242,7 @@ app.add_typer(class_app, name="class")
|
|
|
241
242
|
app.add_typer(queue_app, name="queue")
|
|
242
243
|
app.add_typer(workspace_app, name="workspace")
|
|
243
244
|
app.add_typer(schedule_app, name="schedule")
|
|
245
|
+
app.add_typer(train_app, name="train")
|
|
244
246
|
|
|
245
247
|
# ── Register top-level commands from sub-modules ────────────────────────
|
|
246
248
|
# Commands from generate_app, export_app, and bot_app are registered as
|
|
@@ -298,6 +298,10 @@ def lesson(
|
|
|
298
298
|
homework: bool = typer.Option(
|
|
299
299
|
True, "--homework/--no-homework", help="Include homework"
|
|
300
300
|
),
|
|
301
|
+
multi_agent: bool = typer.Option(
|
|
302
|
+
False, "--multi-agent/--no-multi-agent",
|
|
303
|
+
help="Use multi-agent pipeline (researcher→writer→reviewer) for higher quality",
|
|
304
|
+
),
|
|
301
305
|
fmt: str = typer.Option("markdown", "--format", "-f", help="Export format: markdown, pptx, docx, pdf, handout"),
|
|
302
306
|
):
|
|
303
307
|
"""Generate a detailed daily lesson plan.
|
|
@@ -306,6 +310,9 @@ def lesson(
|
|
|
306
310
|
Standalone mode (no unit plan needed):
|
|
307
311
|
clawed lesson "The American Revolution" --grade 8 --subject "social studies"
|
|
308
312
|
|
|
313
|
+
Multi-agent mode (higher quality, 3x slower):
|
|
314
|
+
clawed lesson "The Missouri Compromise" -g 8 --multi-agent
|
|
315
|
+
|
|
309
316
|
From a unit plan:
|
|
310
317
|
clawed lesson "Photosynthesis" --unit-file output/unit_photosynthesis.json -n 1
|
|
311
318
|
"""
|
|
@@ -388,18 +395,46 @@ def lesson(
|
|
|
388
395
|
|
|
389
396
|
with _safe_progress(console=console) as progress:
|
|
390
397
|
task = progress.add_task(
|
|
391
|
-
f"Generating lesson {lesson_num}...",
|
|
398
|
+
f"Generating lesson {lesson_num}{' [multi-agent]' if multi_agent else ''}...",
|
|
399
|
+
total=None,
|
|
392
400
|
)
|
|
393
401
|
try:
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
402
|
+
if multi_agent:
|
|
403
|
+
# Multi-agent pipeline: researcher → writer → reviewer
|
|
404
|
+
from clawed.multi_agent import multi_agent_generate_master_content
|
|
405
|
+
from clawed.compile_teacher import compile_teacher_view
|
|
406
|
+
console.print("[dim]Using multi-agent pipeline (researcher→writer→reviewer)...[/dim]")
|
|
407
|
+
master = _run_async(
|
|
408
|
+
multi_agent_generate_master_content(
|
|
409
|
+
topic=topic,
|
|
410
|
+
grade=grade,
|
|
411
|
+
subject=subject,
|
|
412
|
+
persona=persona,
|
|
413
|
+
config=None,
|
|
414
|
+
unit_context=kb_prompt_section,
|
|
415
|
+
)
|
|
416
|
+
)
|
|
417
|
+
if master is None:
|
|
418
|
+
console.print("[yellow]Multi-agent returned None, falling back to single-agent...[/yellow]")
|
|
419
|
+
daily = _run_async(
|
|
420
|
+
generate_lesson(
|
|
421
|
+
lesson_number=lesson_num, unit=unit_plan,
|
|
422
|
+
persona=persona, include_homework=homework,
|
|
423
|
+
teacher_materials=kb_prompt_section,
|
|
424
|
+
)
|
|
425
|
+
)
|
|
426
|
+
else:
|
|
427
|
+
daily = compile_teacher_view(master)
|
|
428
|
+
else:
|
|
429
|
+
daily = _run_async(
|
|
430
|
+
generate_lesson(
|
|
431
|
+
lesson_number=lesson_num,
|
|
432
|
+
unit=unit_plan,
|
|
433
|
+
persona=persona,
|
|
434
|
+
include_homework=homework,
|
|
435
|
+
teacher_materials=kb_prompt_section,
|
|
436
|
+
)
|
|
401
437
|
)
|
|
402
|
-
)
|
|
403
438
|
except (RuntimeError, ValueError) as e:
|
|
404
439
|
console.print(f"[red]Generation failed:[/red] {e}")
|
|
405
440
|
console.print("[dim]Run with --debug for full details[/dim]")
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"""Continuous improvement pipeline for Claw-ED fleet agents.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
clawed train --drive Ingest from configured Google Drive folders
|
|
5
|
+
clawed train --path PATH Ingest local materials and refine persona
|
|
6
|
+
clawed train --benchmark Generate & score N lessons for quality check
|
|
7
|
+
clawed train --full Drive ingest + benchmark in sequence
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import random
|
|
14
|
+
from datetime import date
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Optional
|
|
17
|
+
|
|
18
|
+
import typer
|
|
19
|
+
from rich.table import Table
|
|
20
|
+
|
|
21
|
+
from clawed.commands._helpers import console, run_async as _run_async
|
|
22
|
+
|
|
23
|
+
train_app = typer.Typer(help="Continuous improvement pipeline.")
|
|
24
|
+
|
|
25
|
+
_TRAINING_DIR = Path("~/.eduagent/training").expanduser()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# ── Persona refinement helper ───────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
async def _refine_persona(documents: list) -> None:
|
|
31
|
+
"""Extract persona traits from new documents and merge into existing."""
|
|
32
|
+
from clawed.models import AppConfig, TeacherPersona
|
|
33
|
+
from clawed.persona import extract_persona, merge_persona, load_persona, save_persona
|
|
34
|
+
from clawed.commands._helpers import persona_path
|
|
35
|
+
|
|
36
|
+
config = AppConfig.load()
|
|
37
|
+
new_persona = await extract_persona(documents, config)
|
|
38
|
+
|
|
39
|
+
# Load existing persona if available
|
|
40
|
+
pp = persona_path()
|
|
41
|
+
if pp.exists():
|
|
42
|
+
existing = load_persona(pp)
|
|
43
|
+
else:
|
|
44
|
+
existing = TeacherPersona()
|
|
45
|
+
|
|
46
|
+
merged = merge_persona(existing, new_persona)
|
|
47
|
+
save_persona(merged, pp.parent)
|
|
48
|
+
console.print("[green]Persona updated.[/green]")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# ── Drive ingest ────────────────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
async def _drive_ingest() -> list:
|
|
54
|
+
"""Download and ingest from configured Drive URLs. Returns documents."""
|
|
55
|
+
from clawed.models import AppConfig
|
|
56
|
+
from clawed.drive import ingest_drive_folder
|
|
57
|
+
from clawed.ingestor import ingest_path
|
|
58
|
+
|
|
59
|
+
config = AppConfig.load()
|
|
60
|
+
drive_urls = config.teacher_profile.drive_urls
|
|
61
|
+
|
|
62
|
+
if not drive_urls:
|
|
63
|
+
console.print(
|
|
64
|
+
"[yellow]No Drive URLs configured.[/yellow] "
|
|
65
|
+
"Run [bold]clawed config profile[/bold] to add Google Drive folders."
|
|
66
|
+
)
|
|
67
|
+
return []
|
|
68
|
+
|
|
69
|
+
all_docs = []
|
|
70
|
+
for url in drive_urls:
|
|
71
|
+
console.print(f"[dim]Fetching:[/dim] {url}")
|
|
72
|
+
try:
|
|
73
|
+
docs = await ingest_drive_folder(url)
|
|
74
|
+
all_docs.extend(docs)
|
|
75
|
+
console.print(f" -> {len(docs)} documents ingested")
|
|
76
|
+
except Exception as exc:
|
|
77
|
+
console.print(f" [red]Error:[/red] {exc}")
|
|
78
|
+
|
|
79
|
+
if all_docs:
|
|
80
|
+
console.print(f"\n[green]{len(all_docs)} total documents from Drive.[/green]")
|
|
81
|
+
return all_docs
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# ── Path ingest ─────────────────────────────────────────────────────────
|
|
85
|
+
|
|
86
|
+
def _path_ingest(path: Path) -> list:
|
|
87
|
+
"""Ingest local files. Returns documents."""
|
|
88
|
+
from clawed.ingestor import ingest_path
|
|
89
|
+
|
|
90
|
+
if not path.exists():
|
|
91
|
+
console.print(f"[red]Path not found:[/red] {path}")
|
|
92
|
+
raise typer.Exit(1)
|
|
93
|
+
|
|
94
|
+
docs = ingest_path(path)
|
|
95
|
+
console.print(f"[green]{len(docs)} documents ingested from {path}[/green]")
|
|
96
|
+
return docs
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# ── Benchmark ───────────────────────────────────────────────────────────
|
|
100
|
+
|
|
101
|
+
async def _run_benchmark(n: int) -> dict:
|
|
102
|
+
"""Generate N lessons on random topics and score them. Returns report."""
|
|
103
|
+
from clawed.models import AppConfig, UnitPlan, LessonBrief, TeacherPersona
|
|
104
|
+
from clawed.lesson import generate_master_content
|
|
105
|
+
from clawed.quality import score_voice_match
|
|
106
|
+
from clawed.validation import validate_master_content
|
|
107
|
+
from clawed.llm import LLMClient
|
|
108
|
+
from clawed.commands._helpers import load_persona_or_exit
|
|
109
|
+
|
|
110
|
+
persona = load_persona_or_exit()
|
|
111
|
+
config = AppConfig.load()
|
|
112
|
+
client = LLMClient(config)
|
|
113
|
+
|
|
114
|
+
# Build a minimal unit for benchmarking
|
|
115
|
+
subjects = config.teacher_profile.subjects or ["General Studies"]
|
|
116
|
+
subject = random.choice(subjects)
|
|
117
|
+
grades = config.teacher_profile.grade_levels or persona.grade_levels or ["9"]
|
|
118
|
+
grade = random.choice(grades)
|
|
119
|
+
|
|
120
|
+
topics = [
|
|
121
|
+
"Causes of World War I", "Photosynthesis", "Linear Equations",
|
|
122
|
+
"The Civil Rights Movement", "Climate Change", "Shakespeare's Hamlet",
|
|
123
|
+
"The Water Cycle", "Fractions and Decimals", "Ancient Rome",
|
|
124
|
+
"Supply and Demand", "DNA and Genetics", "Poetry Analysis",
|
|
125
|
+
]
|
|
126
|
+
sample_topics = random.sample(topics, min(n, len(topics)))
|
|
127
|
+
|
|
128
|
+
results = []
|
|
129
|
+
persona_ctx = persona.to_prompt_context()
|
|
130
|
+
|
|
131
|
+
for i, topic in enumerate(sample_topics, 1):
|
|
132
|
+
console.print(f"[dim]Generating lesson {i}/{n}: {topic}...[/dim]")
|
|
133
|
+
try:
|
|
134
|
+
unit = UnitPlan(
|
|
135
|
+
title=f"Benchmark: {topic}",
|
|
136
|
+
subject=subject,
|
|
137
|
+
grade_level=grade,
|
|
138
|
+
topic=topic,
|
|
139
|
+
duration_weeks=1,
|
|
140
|
+
overview=f"Benchmark lesson on {topic}",
|
|
141
|
+
daily_lessons=[LessonBrief(
|
|
142
|
+
lesson_number=1,
|
|
143
|
+
topic=topic,
|
|
144
|
+
description=f"Students will understand key aspects of {topic}",
|
|
145
|
+
)],
|
|
146
|
+
)
|
|
147
|
+
mc = await generate_master_content(
|
|
148
|
+
lesson_number=1, unit=unit, persona=persona, config=config,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Score voice match
|
|
152
|
+
lesson_text = mc.model_dump_json()[:3000]
|
|
153
|
+
voice_score = await score_voice_match(lesson_text, persona_ctx, client)
|
|
154
|
+
|
|
155
|
+
# Validate content structure
|
|
156
|
+
errors = validate_master_content(mc, topic)
|
|
157
|
+
pedagogy_score = max(1.0, 5.0 - len(errors) * 0.5)
|
|
158
|
+
diff_score = round((voice_score + pedagogy_score) / 2, 1)
|
|
159
|
+
overall = round((voice_score + pedagogy_score + diff_score) / 3, 1)
|
|
160
|
+
|
|
161
|
+
results.append({
|
|
162
|
+
"topic": topic,
|
|
163
|
+
"voice": round(voice_score, 1),
|
|
164
|
+
"pedagogy": round(pedagogy_score, 1),
|
|
165
|
+
"differentiation": diff_score,
|
|
166
|
+
"overall": overall,
|
|
167
|
+
"errors": errors,
|
|
168
|
+
})
|
|
169
|
+
except Exception as exc:
|
|
170
|
+
console.print(f" [red]Failed:[/red] {exc}")
|
|
171
|
+
results.append({
|
|
172
|
+
"topic": topic,
|
|
173
|
+
"voice": 0.0, "pedagogy": 0.0,
|
|
174
|
+
"differentiation": 0.0, "overall": 0.0,
|
|
175
|
+
"errors": [str(exc)],
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
# Display table
|
|
179
|
+
table = Table(title=f"Benchmark Results ({len(results)} lessons)")
|
|
180
|
+
table.add_column("Topic", style="bold")
|
|
181
|
+
table.add_column("Voice", justify="right")
|
|
182
|
+
table.add_column("Pedagogy", justify="right")
|
|
183
|
+
table.add_column("Differentiation", justify="right")
|
|
184
|
+
table.add_column("Overall", justify="right")
|
|
185
|
+
|
|
186
|
+
for r in results:
|
|
187
|
+
table.add_row(
|
|
188
|
+
r["topic"][:20],
|
|
189
|
+
f"{r['voice']:.1f}",
|
|
190
|
+
f"{r['pedagogy']:.1f}",
|
|
191
|
+
f"{r['differentiation']:.1f}",
|
|
192
|
+
f"{r['overall']:.1f}",
|
|
193
|
+
)
|
|
194
|
+
console.print(table)
|
|
195
|
+
|
|
196
|
+
# Save report
|
|
197
|
+
report = {
|
|
198
|
+
"date": str(date.today()),
|
|
199
|
+
"n": len(results),
|
|
200
|
+
"results": results,
|
|
201
|
+
"avg_overall": round(
|
|
202
|
+
sum(r["overall"] for r in results) / max(len(results), 1), 2
|
|
203
|
+
),
|
|
204
|
+
}
|
|
205
|
+
_TRAINING_DIR.mkdir(parents=True, exist_ok=True)
|
|
206
|
+
report_path = _TRAINING_DIR / f"{date.today()}_train_report.json"
|
|
207
|
+
report_path.write_text(json.dumps(report, indent=2), encoding="utf-8")
|
|
208
|
+
console.print(f"[dim]Report saved: {report_path}[/dim]")
|
|
209
|
+
|
|
210
|
+
return report
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
# ── Main command ────────────────────────────────────────────────────────
|
|
214
|
+
|
|
215
|
+
@train_app.callback(invoke_without_command=True)
|
|
216
|
+
def train(
|
|
217
|
+
drive: bool = typer.Option(False, "--drive", help="Ingest from Google Drive folders"),
|
|
218
|
+
path: Optional[str] = typer.Option(None, "--path", help="Ingest local materials"),
|
|
219
|
+
benchmark: bool = typer.Option(False, "--benchmark", help="Generate & score lessons"),
|
|
220
|
+
n: int = typer.Option(3, "-n", help="Number of benchmark lessons"),
|
|
221
|
+
full: bool = typer.Option(False, "--full", help="Run drive + benchmark"),
|
|
222
|
+
) -> None:
|
|
223
|
+
"""Continuous improvement pipeline for Claw-ED fleet agents."""
|
|
224
|
+
|
|
225
|
+
if not any([drive, path, benchmark, full]):
|
|
226
|
+
console.print(
|
|
227
|
+
"[yellow]Specify a mode:[/yellow] --drive, --path, --benchmark, or --full\n"
|
|
228
|
+
"Run [bold]clawed train --help[/bold] for details."
|
|
229
|
+
)
|
|
230
|
+
raise typer.Exit(0)
|
|
231
|
+
|
|
232
|
+
async def _execute():
|
|
233
|
+
docs = []
|
|
234
|
+
|
|
235
|
+
# Drive ingest
|
|
236
|
+
if drive or full:
|
|
237
|
+
docs = await _drive_ingest()
|
|
238
|
+
if docs:
|
|
239
|
+
await _refine_persona(docs)
|
|
240
|
+
|
|
241
|
+
# Path ingest
|
|
242
|
+
if path:
|
|
243
|
+
local_docs = _path_ingest(Path(path))
|
|
244
|
+
if local_docs:
|
|
245
|
+
await _refine_persona(local_docs)
|
|
246
|
+
docs.extend(local_docs)
|
|
247
|
+
|
|
248
|
+
# Benchmark
|
|
249
|
+
if benchmark or full:
|
|
250
|
+
await _run_benchmark(n)
|
|
251
|
+
|
|
252
|
+
if docs:
|
|
253
|
+
console.print(
|
|
254
|
+
f"\n[bold green]Training complete.[/bold green] "
|
|
255
|
+
f"{len(docs)} documents processed."
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
_run_async(_execute())
|
|
@@ -65,10 +65,35 @@ def _try_keyring_delete(key: str) -> bool:
|
|
|
65
65
|
return False
|
|
66
66
|
|
|
67
67
|
|
|
68
|
+
def _resolve_claude_code_token() -> Optional[str]:
|
|
69
|
+
"""Read fresh OAuth token from Claude Code credential store.
|
|
70
|
+
|
|
71
|
+
Claude Code auto-refreshes OAuth tokens and stores them in
|
|
72
|
+
~/.claude/.credentials.json. Using this as a source means Claw-ED
|
|
73
|
+
never hits expired tokens when Claude Code is installed.
|
|
74
|
+
"""
|
|
75
|
+
import json as _json
|
|
76
|
+
for path in [
|
|
77
|
+
Path.home() / ".claude" / ".credentials.json",
|
|
78
|
+
Path.home() / ".claude.json",
|
|
79
|
+
]:
|
|
80
|
+
if path.exists():
|
|
81
|
+
try:
|
|
82
|
+
data = _json.loads(path.read_text(encoding="utf-8"))
|
|
83
|
+
oauth = data.get("claudeAiOauth", {})
|
|
84
|
+
token = oauth.get("accessToken", "")
|
|
85
|
+
if token and token.startswith("sk-ant-"):
|
|
86
|
+
return token
|
|
87
|
+
except (ValueError, KeyError, OSError):
|
|
88
|
+
continue
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
|
|
68
92
|
def get_api_key(provider: str) -> Optional[str]:
|
|
69
93
|
"""Retrieve an API key for the given provider.
|
|
70
94
|
|
|
71
|
-
Priority: environment variable >
|
|
95
|
+
Priority: environment variable > Claude Code credentials (anthropic only)
|
|
96
|
+
> keyring > secrets.json file.
|
|
72
97
|
"""
|
|
73
98
|
env_map = {
|
|
74
99
|
"anthropic": "ANTHROPIC_API_KEY",
|
|
@@ -86,6 +111,12 @@ def get_api_key(provider: str) -> Optional[str]:
|
|
|
86
111
|
if val:
|
|
87
112
|
return val
|
|
88
113
|
|
|
114
|
+
# Claude Code credential store — auto-refreshing OAuth tokens
|
|
115
|
+
if provider == "anthropic":
|
|
116
|
+
cc_token = _resolve_claude_code_token()
|
|
117
|
+
if cc_token:
|
|
118
|
+
return cc_token
|
|
119
|
+
|
|
89
120
|
key_name = f"{provider}_api_key"
|
|
90
121
|
val = _try_keyring_get(key_name)
|
|
91
122
|
if val:
|
|
@@ -214,12 +245,9 @@ async def test_llm_connection(config: Optional[AppConfig] = None) -> dict:
|
|
|
214
245
|
return _result(False, model, "No API key configured", is_err=True)
|
|
215
246
|
import httpx
|
|
216
247
|
try:
|
|
248
|
+
from clawed.agent import _anthropic_headers
|
|
217
249
|
async with httpx.AsyncClient(timeout=15.0) as client:
|
|
218
|
-
headers =
|
|
219
|
-
"x-api-key": api_key,
|
|
220
|
-
"anthropic-version": "2023-06-01",
|
|
221
|
-
"content-type": "application/json",
|
|
222
|
-
}
|
|
250
|
+
headers = _anthropic_headers(api_key)
|
|
223
251
|
body = {
|
|
224
252
|
"model": model, "max_tokens": 5,
|
|
225
253
|
"messages": [{"role": "user", "content": "Hi"}],
|