@hybridlabor-api/aos 4.4.1 → 4.4.2-beta.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/.agents/skills/firecrawl/SKILL.md +149 -0
- package/.agents/skills/firecrawl/rules/install.md +82 -0
- package/.agents/skills/firecrawl/rules/security.md +26 -0
- package/.agents/skills/firecrawl-agent/SKILL.md +58 -0
- package/.agents/skills/firecrawl-build/SKILL.md +39 -0
- package/.agents/skills/firecrawl-build-interact/SKILL.md +68 -0
- package/.agents/skills/firecrawl-build-onboarding/SKILL.md +103 -0
- package/.agents/skills/firecrawl-build-onboarding/references/auth-flow.md +39 -0
- package/.agents/skills/firecrawl-build-onboarding/references/project-setup.md +20 -0
- package/.agents/skills/firecrawl-build-onboarding/references/sdk-installation.md +17 -0
- package/.agents/skills/firecrawl-build-scrape/SKILL.md +69 -0
- package/.agents/skills/firecrawl-build-search/SKILL.md +69 -0
- package/.agents/skills/firecrawl-crawl/SKILL.md +59 -0
- package/.agents/skills/firecrawl-download/SKILL.md +70 -0
- package/.agents/skills/firecrawl-interact/SKILL.md +84 -0
- package/.agents/skills/firecrawl-map/SKILL.md +51 -0
- package/.agents/skills/firecrawl-scrape/SKILL.md +69 -0
- package/.agents/skills/firecrawl-search/SKILL.md +60 -0
- package/CLAUDE.md +573 -0
- package/README.de.md +0 -8
- package/README.md +0 -8
- package/README.pt.md +0 -8
- package/assets/brand/ao-ant-mark.svg +91 -0
- package/assets/brand/bdb-core-mark.svg +34 -0
- package/assets/brand/heimdall-guardian-mark.svg +16 -0
- package/assets/brand/memb-crystal-mark.svg +18 -0
- package/bin/aos-config.mjs +89 -0
- package/bin/aos-dashboard.mjs +608 -0
- package/bin/aos-uninstall.mjs +227 -0
- package/docs/sessions/audit-2026-09-14-silent-failures.md +114 -0
- package/docs/skills_table.md +0 -3
- package/installer.js +387 -237
- package/mcps/RhinoMCP/cc-plugin/.claude/settings.json +10 -0
- package/mcps/after-effects-mcp/build/index.js +840 -0
- package/mcps/after-effects-mcp/build/scripts/applyEffect.jsx +153 -0
- package/mcps/after-effects-mcp/build/scripts/applyEffectTemplate.jsx +218 -0
- package/mcps/after-effects-mcp/build/scripts/createComposition.jsx +71 -0
- package/mcps/after-effects-mcp/build/scripts/createShapeLayer.jsx +147 -0
- package/mcps/after-effects-mcp/build/scripts/createSolidLayer.jsx +114 -0
- package/mcps/after-effects-mcp/build/scripts/createTextLayer.jsx +115 -0
- package/mcps/after-effects-mcp/build/scripts/getLayerInfo.jsx +192 -0
- package/mcps/after-effects-mcp/build/scripts/getProjectInfo.jsx +90 -0
- package/mcps/after-effects-mcp/build/scripts/listCompositions.jsx +50 -0
- package/mcps/after-effects-mcp/build/scripts/mcp-bridge-auto.jsx +1773 -0
- package/mcps/after-effects-mcp/build/scripts/setLayerProperties.jsx +160 -0
- package/mcps/computer-use-mcp/dist/client.d.ts +150 -0
- package/mcps/computer-use-mcp/dist/client.js +136 -0
- package/mcps/computer-use-mcp/dist/entrypoint.d.ts +16 -0
- package/mcps/computer-use-mcp/dist/entrypoint.js +26 -0
- package/mcps/computer-use-mcp/dist/native.d.ts +212 -0
- package/mcps/computer-use-mcp/dist/native.js +50 -0
- package/mcps/computer-use-mcp/dist/server.d.ts +32 -0
- package/mcps/computer-use-mcp/dist/server.js +342 -0
- package/mcps/computer-use-mcp/dist/session.d.ts +101 -0
- package/mcps/computer-use-mcp/dist/session.js +2372 -0
- package/package.json +13 -4
- package/skills/global_config/aos-project-init/SKILL.md +97 -130
- package/skills/global_config/aos-project-init/assets/openwikiignore.template +62 -0
- package/skills/global_config/aos-project-init/scripts/aos-project-doctor.mjs +38 -1
- package/skills/global_config/aos-setup/SKILL.md +98 -8
- package/skills/global_config/aos-setup/scripts/aos-doctor.mjs +21 -2
- package/skills/global_config/ask-tim/SKILL.md +2 -7
- package/skills/global_config/openwiki-skill/scripts/openwiki_daemon.py +65 -12
- package/bin/setup-saas.mjs +0 -792
- package/mcps/blender-mcp-server/.dockerignore +0 -19
- package/mcps/blender-mcp-server/.github/workflows/ci.yml +0 -67
- package/mcps/blender-mcp-server/.github/workflows/publish-pypi.yml +0 -54
- package/mcps/blender-mcp-server/CHANGELOG.md +0 -64
- package/mcps/blender-mcp-server/CONTRIBUTING.md +0 -107
- package/mcps/blender-mcp-server/Dockerfile +0 -19
- package/mcps/blender-mcp-server/LICENSE +0 -21
- package/mcps/blender-mcp-server/README.md +0 -409
- package/mcps/blender-mcp-server/addon/__init__.py +0 -1440
- package/mcps/blender-mcp-server/addon/models.py +0 -223
- package/mcps/blender-mcp-server/pyproject.toml +0 -120
- package/mcps/blender-mcp-server/scripts/blender_bridge_request.py +0 -71
- package/mcps/blender-mcp-server/scripts/blender_create_test_cube.py +0 -49
- package/mcps/blender-mcp-server/scripts/blender_scene_info.py +0 -38
- package/mcps/blender-mcp-server/scripts/build_addon_zip.sh +0 -29
- package/mcps/blender-mcp-server/scripts/demos/README.md +0 -186
- package/mcps/blender-mcp-server/scripts/demos/dam_break_scene.py +0 -276
- package/mcps/blender-mcp-server/scripts/demos/open_dam_break_gui.py +0 -33
- package/mcps/blender-mcp-server/scripts/demos/pipe_movement_study.py +0 -518
- package/mcps/blender-mcp-server/scripts/demos/pipe_pressure_failure_scene.py +0 -806
- package/mcps/blender-mcp-server/scripts/demos/procedural_dam_break_scene.py +0 -600
- package/mcps/blender-mcp-server/scripts/demos/run_dam_break.py +0 -510
- package/mcps/blender-mcp-server/scripts/launch_blender_gui.py +0 -41
- package/mcps/blender-mcp-server/scripts/library/README.md +0 -64
- package/mcps/blender-mcp-server/scripts/library/apply_transforms.py +0 -46
- package/mcps/blender-mcp-server/scripts/library/camera.py +0 -51
- package/mcps/blender-mcp-server/scripts/library/collections.py +0 -65
- package/mcps/blender-mcp-server/scripts/library/create_mesh.py +0 -41
- package/mcps/blender-mcp-server/scripts/library/effector.py +0 -48
- package/mcps/blender-mcp-server/scripts/library/fluid_domain.py +0 -80
- package/mcps/blender-mcp-server/scripts/library/fluid_inflow.py +0 -98
- package/mcps/blender-mcp-server/scripts/library/frame_range.py +0 -35
- package/mcps/blender-mcp-server/scripts/library/keyframes.py +0 -59
- package/mcps/blender-mcp-server/scripts/library/rigid_body.py +0 -52
- package/mcps/blender-mcp-server/scripts/library/save_blend.py +0 -40
- package/mcps/blender-mcp-server/scripts/server_start.sh +0 -26
- package/mcps/blender-mcp-server/src/blender_mcp_server/__init__.py +0 -3
- package/mcps/blender-mcp-server/src/blender_mcp_server/headless.py +0 -330
- package/mcps/blender-mcp-server/src/blender_mcp_server/server.py +0 -606
- package/mcps/davinci-mcp-professional/.bandit +0 -35
- package/mcps/davinci-mcp-professional/.claude/commands/security-review.md +0 -191
- package/mcps/davinci-mcp-professional/.claude/settings.json +0 -1
- package/mcps/davinci-mcp-professional/.editorconfig +0 -27
- package/mcps/davinci-mcp-professional/.gitattributes +0 -207
- package/mcps/davinci-mcp-professional/.github/CODEOWNERS +0 -2
- package/mcps/davinci-mcp-professional/.github/ISSUE_TEMPLATE/bug_report.md +0 -33
- package/mcps/davinci-mcp-professional/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
- package/mcps/davinci-mcp-professional/.github/PULL_REQUEST_TEMPLATE.md +0 -20
- package/mcps/davinci-mcp-professional/.github/SECURITY.md +0 -97
- package/mcps/davinci-mcp-professional/.github/dependabot.yml +0 -27
- package/mcps/davinci-mcp-professional/.github/workflows/ci.yml +0 -39
- package/mcps/davinci-mcp-professional/ATTRIBUTION.md +0 -4
- package/mcps/davinci-mcp-professional/AUTHORS.md +0 -9
- package/mcps/davinci-mcp-professional/BUGS.md +0 -55
- package/mcps/davinci-mcp-professional/CLAUDE.md +0 -229
- package/mcps/davinci-mcp-professional/CONTRIBUTING.md +0 -51
- package/mcps/davinci-mcp-professional/COPYING.md +0 -228
- package/mcps/davinci-mcp-professional/Doxyfile +0 -379
- package/mcps/davinci-mcp-professional/README.md +0 -143
- package/mcps/davinci-mcp-professional/SECURITY.md +0 -40
- package/mcps/davinci-mcp-professional/USING.md +0 -301
- package/mcps/davinci-mcp-professional/VERSION.md +0 -13
- package/mcps/davinci-mcp-professional/build.py +0 -169
- package/mcps/davinci-mcp-professional/claude_desktop_config_template.json +0 -11
- package/mcps/davinci-mcp-professional/hooks/ci-check.sh +0 -29
- package/mcps/davinci-mcp-professional/hooks/pre-push +0 -46
- package/mcps/davinci-mcp-professional/main.py +0 -40
- package/mcps/davinci-mcp-professional/mcp_server.py +0 -30
- package/mcps/davinci-mcp-professional/pyproject.toml +0 -115
- package/mcps/davinci-mcp-professional/security_audit.py +0 -379
- package/mcps/davinci-mcp-professional/src/davinci_mcp/__init__.py +0 -19
- package/mcps/davinci-mcp-professional/src/davinci_mcp/cli.py +0 -138
- package/mcps/davinci-mcp-professional/src/davinci_mcp/resolve_client.py +0 -342
- package/mcps/davinci-mcp-professional/src/davinci_mcp/resources/__init__.py +0 -60
- package/mcps/davinci-mcp-professional/src/davinci_mcp/server.py +0 -185
- package/mcps/davinci-mcp-professional/src/davinci_mcp/tools/__init__.py +0 -146
- package/mcps/davinci-mcp-professional/src/davinci_mcp/types.py +0 -125
- package/mcps/davinci-mcp-professional/src/davinci_mcp/utils/__init__.py +0 -19
- package/mcps/davinci-mcp-professional/src/davinci_mcp/utils/platform.py +0 -239
- package/mcps/davinci-mcp-professional/test.py +0 -186
- package/mcps/davinci-mcp-professional/uv.lock +0 -2073
- package/mcps/davinci-resolve-mcp/.claude/skills/media-analysis.md +0 -44
- package/mcps/davinci-resolve-mcp/.clinerules +0 -17
- package/mcps/davinci-resolve-mcp/.cursorrules +0 -17
- package/mcps/davinci-resolve-mcp/.github/copilot-instructions.md +0 -17
- package/mcps/davinci-resolve-mcp/.github/workflows/npm-publish.yml +0 -109
- package/mcps/davinci-resolve-mcp/.windsurfrules +0 -17
- package/mcps/davinci-resolve-mcp/AGENTS.md +0 -114
- package/mcps/davinci-resolve-mcp/CHANGELOG.md +0 -2648
- package/mcps/davinci-resolve-mcp/CLAUDE.md +0 -15
- package/mcps/davinci-resolve-mcp/LICENSE +0 -21
- package/mcps/davinci-resolve-mcp/README.md +0 -173
- package/mcps/davinci-resolve-mcp/SECURITY.md +0 -53
- package/mcps/davinci-resolve-mcp/bin/davinci-resolve-mcp.mjs +0 -450
- package/mcps/davinci-resolve-mcp/examples/README.md +0 -53
- package/mcps/davinci-resolve-mcp/examples/markers/README.md +0 -81
- package/mcps/davinci-resolve-mcp/examples/media/README.md +0 -94
- package/mcps/davinci-resolve-mcp/examples/timeline/README.md +0 -98
- package/mcps/davinci-resolve-mcp/install.py +0 -1687
- package/mcps/davinci-resolve-mcp/package.json +0 -52
- package/mcps/davinci-resolve-mcp/release-notes/v2.22.0.md +0 -51
- package/mcps/davinci-resolve-mcp/release-notes/v2.23.0.md +0 -51
- package/mcps/davinci-resolve-mcp/release-notes/v2.23.1.md +0 -35
- package/mcps/davinci-resolve-mcp/release-notes/v2.24.1.md +0 -32
- package/mcps/davinci-resolve-mcp/scripts/audit_api_parity.py +0 -275
- package/mcps/davinci-resolve-mcp/scripts/audit_readwrite_symmetry.py +0 -80
- package/mcps/davinci-resolve-mcp/scripts/doctor.py +0 -227
- package/mcps/davinci-resolve-mcp/scripts/gen_api_limitations.py +0 -144
- package/mcps/davinci-resolve-mcp/scripts/live_media_analysis_polish_probe.py +0 -65
- package/mcps/davinci-resolve-mcp/scripts/measure_bridge_cost.py +0 -65
- package/mcps/davinci-resolve-mcp/scripts/regen_panel_screenshots.py +0 -76
- package/mcps/davinci-resolve-mcp/src/__init__.py +0 -3
- package/mcps/davinci-resolve-mcp/src/analysis_dashboard.py +0 -15505
- package/mcps/davinci-resolve-mcp/src/batch_cli.py +0 -540
- package/mcps/davinci-resolve-mcp/src/control_panel.py +0 -13
- package/mcps/davinci-resolve-mcp/src/granular/__init__.py +0 -17
- package/mcps/davinci-resolve-mcp/src/granular/common.py +0 -741
- package/mcps/davinci-resolve-mcp/src/granular/folder.py +0 -420
- package/mcps/davinci-resolve-mcp/src/granular/gallery.py +0 -306
- package/mcps/davinci-resolve-mcp/src/granular/graph.py +0 -309
- package/mcps/davinci-resolve-mcp/src/granular/media_pool.py +0 -679
- package/mcps/davinci-resolve-mcp/src/granular/media_pool_item.py +0 -1020
- package/mcps/davinci-resolve-mcp/src/granular/media_storage.py +0 -179
- package/mcps/davinci-resolve-mcp/src/granular/project.py +0 -1654
- package/mcps/davinci-resolve-mcp/src/granular/resolve_control.py +0 -538
- package/mcps/davinci-resolve-mcp/src/granular/timeline.py +0 -1075
- package/mcps/davinci-resolve-mcp/src/granular/timeline_item.py +0 -2251
- package/mcps/davinci-resolve-mcp/src/resolve_mcp_server.py +0 -43
- package/mcps/davinci-resolve-mcp/src/server.py +0 -24116
- package/mcps/davinci-resolve-mcp/src/utils/__init__.py +0 -3
- package/mcps/davinci-resolve-mcp/src/utils/actor_identity.py +0 -46
- package/mcps/davinci-resolve-mcp/src/utils/analysis_caps.py +0 -678
- package/mcps/davinci-resolve-mcp/src/utils/analysis_memory.py +0 -755
- package/mcps/davinci-resolve-mcp/src/utils/analysis_runs.py +0 -307
- package/mcps/davinci-resolve-mcp/src/utils/analysis_store.py +0 -945
- package/mcps/davinci-resolve-mcp/src/utils/api_truth.py +0 -525
- package/mcps/davinci-resolve-mcp/src/utils/app_control.py +0 -319
- package/mcps/davinci-resolve-mcp/src/utils/audio_fairlight_live_probe.py +0 -263
- package/mcps/davinci-resolve-mcp/src/utils/brain_edits.py +0 -387
- package/mcps/davinci-resolve-mcp/src/utils/bridge_metrics.py +0 -76
- package/mcps/davinci-resolve-mcp/src/utils/cdl.py +0 -20
- package/mcps/davinci-resolve-mcp/src/utils/clip_query.py +0 -85
- package/mcps/davinci-resolve-mcp/src/utils/cloud_operations.py +0 -192
- package/mcps/davinci-resolve-mcp/src/utils/color_grade_live_probe.py +0 -444
- package/mcps/davinci-resolve-mcp/src/utils/contracts.py +0 -102
- package/mcps/davinci-resolve-mcp/src/utils/cut_ir.py +0 -115
- package/mcps/davinci-resolve-mcp/src/utils/dctl_templates.py +0 -368
- package/mcps/davinci-resolve-mcp/src/utils/deep_vision.py +0 -643
- package/mcps/davinci-resolve-mcp/src/utils/destructive_hook.py +0 -606
- package/mcps/davinci-resolve-mcp/src/utils/edit_engine.py +0 -737
- package/mcps/davinci-resolve-mcp/src/utils/embeddings.py +0 -922
- package/mcps/davinci-resolve-mcp/src/utils/entities.py +0 -579
- package/mcps/davinci-resolve-mcp/src/utils/extension_authoring_live_probe.py +0 -292
- package/mcps/davinci-resolve-mcp/src/utils/failure_tracker.py +0 -119
- package/mcps/davinci-resolve-mcp/src/utils/fuse_templates.py +0 -1968
- package/mcps/davinci-resolve-mcp/src/utils/fusion_composition_live_probe.py +0 -284
- package/mcps/davinci-resolve-mcp/src/utils/fusion_group_settings.py +0 -334
- package/mcps/davinci-resolve-mcp/src/utils/layout_presets.py +0 -333
- package/mcps/davinci-resolve-mcp/src/utils/mcp_stdio.py +0 -32
- package/mcps/davinci-resolve-mcp/src/utils/mcp_transport.py +0 -128
- package/mcps/davinci-resolve-mcp/src/utils/media_analysis.py +0 -7690
- package/mcps/davinci-resolve-mcp/src/utils/media_analysis_jobs.py +0 -805
- package/mcps/davinci-resolve-mcp/src/utils/media_pool_changes.py +0 -121
- package/mcps/davinci-resolve-mcp/src/utils/media_pool_ingest_live_probe.py +0 -592
- package/mcps/davinci-resolve-mcp/src/utils/multicam.py +0 -393
- package/mcps/davinci-resolve-mcp/src/utils/object_inspection.py +0 -301
- package/mcps/davinci-resolve-mcp/src/utils/page_lock.py +0 -75
- package/mcps/davinci-resolve-mcp/src/utils/platform.py +0 -157
- package/mcps/davinci-resolve-mcp/src/utils/proc.py +0 -26
- package/mcps/davinci-resolve-mcp/src/utils/project_cleanup.py +0 -73
- package/mcps/davinci-resolve-mcp/src/utils/project_lifecycle_live_probe.py +0 -376
- package/mcps/davinci-resolve-mcp/src/utils/project_lint.py +0 -131
- package/mcps/davinci-resolve-mcp/src/utils/project_properties.py +0 -601
- package/mcps/davinci-resolve-mcp/src/utils/project_spec.py +0 -459
- package/mcps/davinci-resolve-mcp/src/utils/readback.py +0 -91
- package/mcps/davinci-resolve-mcp/src/utils/render_deliver_live_probe.py +0 -384
- package/mcps/davinci-resolve-mcp/src/utils/resolve_ai_governance.py +0 -214
- package/mcps/davinci-resolve-mcp/src/utils/resolve_ai_ledger.py +0 -244
- package/mcps/davinci-resolve-mcp/src/utils/resolve_busy.py +0 -171
- package/mcps/davinci-resolve-mcp/src/utils/resolve_connection.py +0 -77
- package/mcps/davinci-resolve-mcp/src/utils/review_annotation_live_probe.py +0 -352
- package/mcps/davinci-resolve-mcp/src/utils/script_templates.py +0 -1193
- package/mcps/davinci-resolve-mcp/src/utils/shot_relationships.py +0 -544
- package/mcps/davinci-resolve-mcp/src/utils/structural_diff.py +0 -175
- package/mcps/davinci-resolve-mcp/src/utils/sync_detection.py +0 -889
- package/mcps/davinci-resolve-mcp/src/utils/timeline_brain_db.py +0 -826
- package/mcps/davinci-resolve-mcp/src/utils/timeline_conform_live_probe.py +0 -280
- package/mcps/davinci-resolve-mcp/src/utils/timeline_kernel_live_probe.py +0 -1092
- package/mcps/davinci-resolve-mcp/src/utils/timeline_kernel_probe.py +0 -185
- package/mcps/davinci-resolve-mcp/src/utils/timeline_title_text.py +0 -87
- package/mcps/davinci-resolve-mcp/src/utils/timeline_versioning.py +0 -730
- package/mcps/davinci-resolve-mcp/src/utils/update_check.py +0 -685
- package/mcps/vectorworks-mcp/.dockerignore +0 -8
- package/mcps/vectorworks-mcp/Dockerfile +0 -28
- package/mcps/vectorworks-mcp/README.md +0 -156
- package/mcps/vectorworks-mcp/app/__init__.py +0 -2
- package/mcps/vectorworks-mcp/app/api.py +0 -52
- package/mcps/vectorworks-mcp/app/chunking.py +0 -56
- package/mcps/vectorworks-mcp/app/config.py +0 -29
- package/mcps/vectorworks-mcp/app/embeddings.py +0 -28
- package/mcps/vectorworks-mcp/app/indexer.py +0 -106
- package/mcps/vectorworks-mcp/app/main.py +0 -33
- package/mcps/vectorworks-mcp/app/mcp_server.py +0 -71
- package/mcps/vectorworks-mcp/app/search.py +0 -54
- package/mcps/vectorworks-mcp/app/utils.py +0 -14
- package/mcps/vectorworks-mcp/data/.gitkeep +0 -1
- package/mcps/vectorworks-mcp/data/vw-app-help/VW2022_PluginScript.htm +0 -95
- package/mcps/vectorworks-mcp/data/vw-app-help/VW2022_ScriptEditor.htm +0 -266
- package/mcps/vectorworks-mcp/data/vw-app-help/VW2022_Scripting.htm +0 -106
- package/mcps/vectorworks-mcp/data/vw-app-help/VW2023_CreatingScriptedPlugins.htm +0 -327
- package/mcps/vectorworks-mcp/data/vw-app-help/VW2024_RunningScripts.htm +0 -116
- package/mcps/vectorworks-mcp/data/vw-devwiki/VS_Function_Reference_category.html +0 -2129
- package/mcps/vectorworks-mcp/data/vw-jp-ref/ObjectsSymbols.html +0 -2035
- package/mcps/vectorworks-mcp/data/vw-jp-ref/ScriptFunctionReference.html +0 -29
- package/mcps/vectorworks-mcp/design.md +0 -57
- package/mcps/vectorworks-mcp/docker-compose.yml +0 -18
- package/mcps/vectorworks-mcp/index/.gitkeep +0 -1
- package/mcps/vectorworks-mcp/index/meta.jsonl +0 -3956
- package/mcps/vectorworks-mcp/index/vw.faiss +0 -0
- package/mcps/vectorworks-mcp/requirements.txt +0 -10
- package/mcps/vectorworks-mcp/scripts/fetch_docs_minimal.sh +0 -84
- package/mcps/vectorworks-mcp/scripts/fetch_github_vectorworks.sh +0 -85
- package/mcps/vectorworks-mcp/templates/index.html +0 -40
- package/scripts/ecosystem-health-audit.js +0 -257
- package/skills/bdb-dev-os-skill/SKILL.md +0 -49
- package/skills/bdb-dev-os-skill/package.json +0 -11
- package/skills/bdbsaastraining/SKILL.md +0 -316
- package/skills/bdbsaastraining/references/exam-pool.md +0 -313
- package/skills/bdbsaastraining/references/interaction-modes.md +0 -94
- package/skills/bdbsaastraining/references/track-a-ai-agent.md +0 -111
- package/skills/bdbsaastraining/references/track-b-wordpress.md +0 -111
- package/skills/bdbsaastraining/references/track-c-mailserver.md +0 -107
- package/skills/bdbsaastraining/references/track-d-debian.md +0 -109
- package/skills/bdbsaastraining/references/track-e-custom.md +0 -83
- package/skills/bdbsaastraining/scripts/build_profile.py +0 -529
- package/skills/bdbsaastraining/scripts/generate_certificate.py +0 -161
- package/skills/bdbsaastraining/scripts/preflight_check.sh +0 -150
- package/skills/bdbsaastraining/templates/certificate_template.html +0 -508
- package/skills/global_config/bdb-ecosystem-health/SKILL.md +0 -68
- package/skills/global_config/bdbsaas-ops/SKILL.md +0 -16
- package/skills/global_config/bdbsaashost/SKILL.md +0 -125
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
FROM python:3.11-slim
|
|
2
|
-
|
|
3
|
-
ENV PYTHONDONTWRITEBYTECODE=1 \
|
|
4
|
-
PYTHONUNBUFFERED=1
|
|
5
|
-
|
|
6
|
-
WORKDIR /app
|
|
7
|
-
|
|
8
|
-
# System deps for faiss/numpy
|
|
9
|
-
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
10
|
-
build-essential \
|
|
11
|
-
g++ \
|
|
12
|
-
libopenblas-dev \
|
|
13
|
-
git \
|
|
14
|
-
curl \
|
|
15
|
-
ca-certificates \
|
|
16
|
-
&& rm -rf /var/lib/apt/lists/*
|
|
17
|
-
|
|
18
|
-
COPY requirements.txt ./
|
|
19
|
-
RUN pip install --no-cache-dir -r requirements.txt
|
|
20
|
-
|
|
21
|
-
COPY . .
|
|
22
|
-
|
|
23
|
-
# Default directories
|
|
24
|
-
RUN mkdir -p /app/data /app/index
|
|
25
|
-
|
|
26
|
-
EXPOSE 8000 8765
|
|
27
|
-
|
|
28
|
-
CMD ["python", "-m", "app.main"]
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
# Vectorworks RAG + MCP
|
|
2
|
-
|
|
3
|
-
- Vectorworks の Python / VectorScript ドキュメントをローカルに取り込み、FAISS で横断検索します。
|
|
4
|
-
- FastAPI で `/search` `/answer` `/get` を提供し、簡易 Web UI から検索・出典確認できます。
|
|
5
|
-
- WebSocket(JSON-RPC 2.0) の MCP サーバーを同時起動し、`vw.search` / `vw.answer` / `vw.get` ツールを提供します。
|
|
6
|
-
- docker compose で `app`(API/MCP)と `db`(Postgres 16)を同時起動します
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
## 必要要件
|
|
11
|
-
|
|
12
|
-
- Docker / Docker Compose(v2)
|
|
13
|
-
|
|
14
|
-
## クイックスタート
|
|
15
|
-
|
|
16
|
-
1. ビルド
|
|
17
|
-
|
|
18
|
-
- `docker compose build`
|
|
19
|
-
|
|
20
|
-
2. 文書取得
|
|
21
|
-
|
|
22
|
-
- `docker compose run --rm app bash scripts/fetch_docs_minimal.sh`
|
|
23
|
-
- `docker compose run --rm -e GITHUB_TOKEN="$GITHUB_TOKEN" app bash scripts/fetch_github_vectorworks.sh`
|
|
24
|
-
|
|
25
|
-
3. ベクター生成(埋め込み + FAISS)
|
|
26
|
-
|
|
27
|
-
- `docker compose run --rm app python -m app.indexer`
|
|
28
|
-
- 既にインデックスがある状態でドキュメントを追加した場合は再作成してください:
|
|
29
|
-
- `docker compose run --rm app python -m app.indexer --rebuild`
|
|
30
|
-
|
|
31
|
-
4. 起動(UI + MCP + Postgres)
|
|
32
|
-
|
|
33
|
-
- `docker compose up`
|
|
34
|
-
|
|
35
|
-
5. アクセス
|
|
36
|
-
|
|
37
|
-
- UI: `http://localhost:8000`
|
|
38
|
-
- MCP: `ws://localhost:8765`
|
|
39
|
-
|
|
40
|
-
補足
|
|
41
|
-
|
|
42
|
-
- 2. と 3) は次のワンライナーでも可:
|
|
43
|
-
- `docker compose run --rm app bash -lc 'scripts/fetch_docs_minimal.sh && python -m app.indexer'`
|
|
44
|
-
|
|
45
|
-
## ドキュメントの取得
|
|
46
|
-
|
|
47
|
-
以下のコマンドで、必要最小限のドキュメント(md/html のみ)を `data/` に取得します。
|
|
48
|
-
|
|
49
|
-
- 依存: `git`, `curl`
|
|
50
|
-
- 実行(コンテナ内):
|
|
51
|
-
- `docker compose run --rm app bash scripts/fetch_docs_minimal.sh`
|
|
52
|
-
- ネットワーク事情で 403 が発生する場合、スクリプトは該当ページをスキップして続行します。
|
|
53
|
-
- 必要なら UA を上書き可能: `docker compose run --rm -e UA="Mozilla/5.0 ..." app bash scripts/fetch_docs_minimal.sh`
|
|
54
|
-
|
|
55
|
-
取得対象(要点のみ)
|
|
56
|
-
|
|
57
|
-
- GitHub: Vectorworks/developer-scripting(入門/導線の Markdown)
|
|
58
|
-
- App Help: Scripting の基本導線(2022/2023/2024 の要点ページ)
|
|
59
|
-
- 日本語サイト: VectorScript 関数索引ページ + 例示ページ
|
|
60
|
-
- Developer Wiki: VS Function Reference カテゴリインデックス(HTML)
|
|
61
|
-
|
|
62
|
-
注意
|
|
63
|
-
|
|
64
|
-
- インデクサは `.md` `.markdown` `.html` `.htm` `.txt` のみ対応です。PDF 等は対象外です。
|
|
65
|
-
|
|
66
|
-
## GitHub(Vectorworks 指定リポのみ取得)
|
|
67
|
-
|
|
68
|
-
- 目的: 次の3つのリポジトリのみを `data/github/vectorworks/` にクローン(または更新)します。
|
|
69
|
-
- `Vectorworks/developer-scripting`
|
|
70
|
-
- `Vectorworks/developer-sdk`
|
|
71
|
-
- `Vectorworks/developer-worksheets`
|
|
72
|
-
- 実行(コンテナ内):
|
|
73
|
-
- `docker compose run --rm app bash scripts/fetch_github_vectorworks.sh`
|
|
74
|
-
- 取得先: `data/github/vectorworks/<repo>`
|
|
75
|
-
- 追加/変更したい場合は環境変数 `REPOS` で指定できます(スペースまたはカンマ区切り)。
|
|
76
|
-
- 例: `docker compose run --rm -e REPOS="Vectorworks/developer-scripting,Vectorworks/developer-sdk" app bash scripts/fetch_github_vectorworks.sh`
|
|
77
|
-
- 更新方式の指定(任意): `UPDATE_MODE=pull`(既定)または `UPDATE_MODE=reset`
|
|
78
|
-
- `pull`: `git pull --ff-only --depth=1 --prune` による高速更新(ローカル変更がある場合は失敗→フェールセーフで fetch+reset)
|
|
79
|
-
- `reset`: `fetch --depth=1` 後に `reset --hard` で完全同期(ローカル変更を破棄)
|
|
80
|
-
|
|
81
|
-
注意
|
|
82
|
-
|
|
83
|
-
- インデクサはテキスト系拡張子(.md/.html/.txt 等)のみ対象とします。
|
|
84
|
-
|
|
85
|
-
## ベクター生成(埋め込み + FAISS インデックス)
|
|
86
|
-
|
|
87
|
-
- 実行(コンテナ内):
|
|
88
|
-
- `docker compose run --rm app python -m app.indexer`
|
|
89
|
-
|
|
90
|
-
実行後、`index/` に `vw.faiss` と `meta.jsonl` が作成されます。
|
|
91
|
-
(先に `bash scripts/fetch_docs_minimal.sh` などで `data/` に文書がある状態にしてください)
|
|
92
|
-
|
|
93
|
-
## 起動(UI + MCP + Postgres)
|
|
94
|
-
|
|
95
|
-
- `docker compose up --build`
|
|
96
|
-
- UI: `http://localhost:8000`
|
|
97
|
-
- MCP: `ws://localhost:8765`
|
|
98
|
-
- Postgres はコンテナ内部ネットワークで起動(ホストへ未公開)
|
|
99
|
-
|
|
100
|
-
## API 例
|
|
101
|
-
|
|
102
|
-
- 検索: `GET /search?q=PushAttrs&k=6`
|
|
103
|
-
- 例: `curl -s "http://localhost:8000/search?q=VectorScript&k=6" | jq .`
|
|
104
|
-
- 回答(ドラフト): `GET /answer?q=...&k=6`
|
|
105
|
-
- 例: `curl -s "http://localhost:8000/answer?q=record+format" | jq .`
|
|
106
|
-
- チャンク取得: `GET /get?doc_id=...&chunk_id=...`
|
|
107
|
-
|
|
108
|
-
UI は `GET /` にあり、検索フォームから同等の結果を確認できます。
|
|
109
|
-
|
|
110
|
-
## MCP(Model Context Protocol)
|
|
111
|
-
|
|
112
|
-
- VS Code から接続: 次のコマンドで MCP を追加
|
|
113
|
-
- `code --add-mcp '{"name":"vw_docs_local","url":"ws://localhost:8765"}'`
|
|
114
|
-
- サポートするツール
|
|
115
|
-
- `vw.search({ query, k? })`
|
|
116
|
-
- `vw.answer({ query, k? })`
|
|
117
|
-
- `vw.get({ doc_id, chunk_id })`
|
|
118
|
-
|
|
119
|
-
実装は JSON-RPC 2.0 ベースの WebSocket サーバーです(`app/mcp_server.py`)。
|
|
120
|
-
|
|
121
|
-
## ディレクトリ構成
|
|
122
|
-
|
|
123
|
-
- `app/` アプリ本体
|
|
124
|
-
- `api.py` FastAPI アプリ
|
|
125
|
-
- `mcp_server.py` MCP(WebSocket) サーバー
|
|
126
|
-
- `indexer.py` 取込・ベクター化(埋め込み + FAISS 作成)
|
|
127
|
-
- `search.py` 検索/回答のコアロジック
|
|
128
|
-
- `chunking.py` チャンク分割(約 700 トークン相当の文字長を目安)
|
|
129
|
-
- `templates/` Web UI テンプレート
|
|
130
|
-
- `data/` 原文の md/html(相対パスが `doc_id`)
|
|
131
|
-
- `index/` FAISS とメタデータ(`vw.faiss`, `meta.jsonl`)
|
|
132
|
-
|
|
133
|
-
## 環境変数(任意)
|
|
134
|
-
|
|
135
|
-
- `DATA_DIR` データ配置ディレクトリ(既定: `data`)
|
|
136
|
-
- `INDEX_DIR` インデックス出力ディレクトリ(既定: `index`)
|
|
137
|
-
- `EMBED_MODEL` 埋め込みモデル(既定: `sentence-transformers/all-MiniLM-L6-v2`)
|
|
138
|
-
- `CHUNK_CHARS` チャンク文字長の目安(既定: `2800` ≒ 700 トークン)
|
|
139
|
-
- `CHUNK_OVERLAP` チャンクの文字オーバーラップ(既定: `480`)
|
|
140
|
-
- `API_HOST` / `API_PORT` FastAPI バインド(既定: `0.0.0.0:8000`)
|
|
141
|
-
- `MCP_HOST` / `MCP_PORT` MCP バインド(既定: `0.0.0.0:8765`)
|
|
142
|
-
|
|
143
|
-
Postgres(将来用)
|
|
144
|
-
|
|
145
|
-
- `PGHOST` / `PGPORT` / `PGDATABASE` / `PGUSER` / `PGPASSWORD`
|
|
146
|
-
|
|
147
|
-
## 運用メモ
|
|
148
|
-
|
|
149
|
-
- ドキュメントを更新したら、再度ベクター生成: `python -m app.indexer`
|
|
150
|
-
- 初回は埋め込みモデルのダウンロードで時間がかかる場合があります。
|
|
151
|
-
- CPU で実行する前提のため、FAISS は `IndexFlatIP` + 正規化ベクター(cosine 相当)を使用しています。
|
|
152
|
-
|
|
153
|
-
## ライセンス / 注意
|
|
154
|
-
|
|
155
|
-
- このリポジトリはドキュメントを含みません。各自の利用規約・著作権に従って `data/` に配置してください。
|
|
156
|
-
- `answer` は抜粋ベースの簡易ドラフトです。最終判断は原文を確認してください。
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
from typing import Optional
|
|
5
|
-
|
|
6
|
-
from fastapi import FastAPI, HTTPException, Query, Request
|
|
7
|
-
from fastapi.responses import HTMLResponse
|
|
8
|
-
from fastapi.templating import Jinja2Templates
|
|
9
|
-
|
|
10
|
-
from .config import settings
|
|
11
|
-
from .embeddings import Embedder
|
|
12
|
-
from .indexer import IndexPaths, ensure_index
|
|
13
|
-
from .search import Searcher
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def create_app() -> FastAPI:
|
|
17
|
-
app = FastAPI(title="Vectorworks Docs RAG")
|
|
18
|
-
|
|
19
|
-
templates = Jinja2Templates(directory=str(Path("templates")))
|
|
20
|
-
|
|
21
|
-
embedder = Embedder()
|
|
22
|
-
index, meta = ensure_index(embedder)
|
|
23
|
-
searcher = Searcher(index=index, meta=meta, embedder=embedder)
|
|
24
|
-
|
|
25
|
-
@app.get("/", response_class=HTMLResponse)
|
|
26
|
-
async def home(request: Request, q: Optional[str] = None, k: int = 6):
|
|
27
|
-
results = searcher.search(q, k=k) if q else []
|
|
28
|
-
return templates.TemplateResponse(
|
|
29
|
-
"index.html",
|
|
30
|
-
{"request": request, "q": q or "", "results": results},
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
@app.get("/search")
|
|
34
|
-
async def api_search(q: str = Query(...), k: int = 6):
|
|
35
|
-
return {"query": q, "results": searcher.search(q, k=k)}
|
|
36
|
-
|
|
37
|
-
@app.get("/answer")
|
|
38
|
-
async def api_answer(q: str = Query(...), k: int = 6):
|
|
39
|
-
return searcher.answer(q, k=k)
|
|
40
|
-
|
|
41
|
-
@app.get("/get")
|
|
42
|
-
async def api_get(doc_id: str, chunk_id: int):
|
|
43
|
-
try:
|
|
44
|
-
return searcher.get(doc_id, chunk_id)
|
|
45
|
-
except KeyError as e:
|
|
46
|
-
raise HTTPException(status_code=404, detail=str(e))
|
|
47
|
-
|
|
48
|
-
return app
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
app = create_app()
|
|
52
|
-
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import re
|
|
4
|
-
from typing import Iterable, List
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def split_paragraphs(text: str) -> List[str]:
|
|
8
|
-
# Normalize line endings and split on blank lines
|
|
9
|
-
parts = re.split(r"\n\s*\n+", text.strip())
|
|
10
|
-
return [p.strip() for p in parts if p.strip()]
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def merge_to_chunks(paragraphs: Iterable[str], max_chars: int, overlap: int) -> List[str]:
|
|
14
|
-
chunks: List[str] = []
|
|
15
|
-
buf: List[str] = []
|
|
16
|
-
buf_len = 0
|
|
17
|
-
|
|
18
|
-
for p in paragraphs:
|
|
19
|
-
# If a single paragraph is larger than max, hard-split by sentences
|
|
20
|
-
if len(p) > max_chars:
|
|
21
|
-
sentences = re.split(r"(?<=[。..!?])\s+", p)
|
|
22
|
-
p_parts: List[str] = []
|
|
23
|
-
cur = ""
|
|
24
|
-
for s in sentences:
|
|
25
|
-
if not s:
|
|
26
|
-
continue
|
|
27
|
-
if len(cur) + len(s) + 1 > max_chars and cur:
|
|
28
|
-
p_parts.append(cur)
|
|
29
|
-
cur = s
|
|
30
|
-
else:
|
|
31
|
-
cur = (cur + " " + s).strip()
|
|
32
|
-
if cur:
|
|
33
|
-
p_parts.append(cur)
|
|
34
|
-
else:
|
|
35
|
-
p_parts = [p]
|
|
36
|
-
|
|
37
|
-
for part in p_parts:
|
|
38
|
-
if buf_len + len(part) + 2 > max_chars and buf:
|
|
39
|
-
chunks.append("\n\n".join(buf))
|
|
40
|
-
# overlap by characters from the end of the previous chunk
|
|
41
|
-
if overlap > 0:
|
|
42
|
-
tail = chunks[-1][-overlap:]
|
|
43
|
-
buf = [tail, part]
|
|
44
|
-
buf_len = len(tail) + len(part) + 2
|
|
45
|
-
else:
|
|
46
|
-
buf = [part]
|
|
47
|
-
buf_len = len(part)
|
|
48
|
-
else:
|
|
49
|
-
buf.append(part)
|
|
50
|
-
buf_len += len(part) + 2
|
|
51
|
-
|
|
52
|
-
if buf:
|
|
53
|
-
chunks.append("\n\n".join(buf))
|
|
54
|
-
|
|
55
|
-
return chunks
|
|
56
|
-
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
from dataclasses import dataclass
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
@dataclass
|
|
6
|
-
class Settings:
|
|
7
|
-
# Paths
|
|
8
|
-
data_dir: str = os.getenv("DATA_DIR", "data")
|
|
9
|
-
index_dir: str = os.getenv("INDEX_DIR", "index")
|
|
10
|
-
index_file: str = os.getenv("INDEX_FILE", "vw.faiss")
|
|
11
|
-
meta_file: str = os.getenv("META_FILE", "meta.jsonl")
|
|
12
|
-
|
|
13
|
-
# Embeddings
|
|
14
|
-
model_name: str = os.getenv("EMBED_MODEL", "sentence-transformers/all-MiniLM-L6-v2")
|
|
15
|
-
batch_size: int = int(os.getenv("EMBED_BATCH", "32"))
|
|
16
|
-
|
|
17
|
-
# Chunking
|
|
18
|
-
chunk_chars: int = int(os.getenv("CHUNK_CHARS", "2800"))
|
|
19
|
-
chunk_overlap: int = int(os.getenv("CHUNK_OVERLAP", "480"))
|
|
20
|
-
|
|
21
|
-
# API / MCP
|
|
22
|
-
api_host: str = os.getenv("API_HOST", "0.0.0.0")
|
|
23
|
-
api_port: int = int(os.getenv("API_PORT", "8000"))
|
|
24
|
-
mcp_host: str = os.getenv("MCP_HOST", "0.0.0.0")
|
|
25
|
-
mcp_port: int = int(os.getenv("MCP_PORT", "8765"))
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
settings = Settings()
|
|
29
|
-
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
from typing import List
|
|
5
|
-
|
|
6
|
-
import numpy as np
|
|
7
|
-
from sentence_transformers import SentenceTransformer
|
|
8
|
-
|
|
9
|
-
from .config import settings
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@dataclass
|
|
13
|
-
class Embedder:
|
|
14
|
-
model_name: str = settings.model_name
|
|
15
|
-
batch_size: int = settings.batch_size
|
|
16
|
-
|
|
17
|
-
def __post_init__(self):
|
|
18
|
-
self.model = SentenceTransformer(self.model_name, device="cpu")
|
|
19
|
-
|
|
20
|
-
def encode(self, texts: List[str]) -> np.ndarray:
|
|
21
|
-
vectors = self.model.encode(
|
|
22
|
-
texts,
|
|
23
|
-
batch_size=self.batch_size,
|
|
24
|
-
show_progress_bar=False,
|
|
25
|
-
normalize_embeddings=True, # cosine via dot product
|
|
26
|
-
)
|
|
27
|
-
return np.asarray(vectors, dtype=np.float32)
|
|
28
|
-
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import argparse
|
|
4
|
-
import json
|
|
5
|
-
from dataclasses import dataclass
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from typing import List, Tuple
|
|
8
|
-
|
|
9
|
-
import faiss # type: ignore
|
|
10
|
-
import numpy as np
|
|
11
|
-
|
|
12
|
-
from .chunking import merge_to_chunks, split_paragraphs
|
|
13
|
-
from .config import settings
|
|
14
|
-
from .embeddings import Embedder
|
|
15
|
-
from .utils import iter_text_files
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
@dataclass
|
|
19
|
-
class IndexPaths:
|
|
20
|
-
base: Path
|
|
21
|
-
index_file: Path
|
|
22
|
-
meta_file: Path
|
|
23
|
-
|
|
24
|
-
@staticmethod
|
|
25
|
-
def from_settings() -> "IndexPaths":
|
|
26
|
-
base = Path(settings.index_dir)
|
|
27
|
-
return IndexPaths(
|
|
28
|
-
base=base,
|
|
29
|
-
index_file=base / settings.index_file,
|
|
30
|
-
meta_file=base / settings.meta_file,
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def build_index(data_dir: Path, index_paths: IndexPaths, embedder: Embedder) -> Tuple[faiss.Index, List[dict]]:
|
|
35
|
-
index_paths.base.mkdir(parents=True, exist_ok=True)
|
|
36
|
-
|
|
37
|
-
texts: List[str] = []
|
|
38
|
-
meta: List[dict] = []
|
|
39
|
-
|
|
40
|
-
for src in iter_text_files(data_dir):
|
|
41
|
-
rel = src.relative_to(data_dir).as_posix()
|
|
42
|
-
content = src.read_text(encoding="utf-8", errors="ignore")
|
|
43
|
-
paragraphs = split_paragraphs(content)
|
|
44
|
-
chunks = merge_to_chunks(paragraphs, settings.chunk_chars, settings.chunk_overlap)
|
|
45
|
-
for i, chunk in enumerate(chunks):
|
|
46
|
-
texts.append(chunk)
|
|
47
|
-
meta.append({"doc_id": rel, "chunk_id": i, "text": chunk})
|
|
48
|
-
|
|
49
|
-
if not texts:
|
|
50
|
-
raise RuntimeError(f"No ingestable files found under {data_dir}")
|
|
51
|
-
|
|
52
|
-
vectors = embedder.encode(texts)
|
|
53
|
-
dim = vectors.shape[1]
|
|
54
|
-
index = faiss.IndexFlatIP(dim)
|
|
55
|
-
index.add(vectors)
|
|
56
|
-
|
|
57
|
-
# persist
|
|
58
|
-
faiss.write_index(index, str(index_paths.index_file))
|
|
59
|
-
with index_paths.meta_file.open("w", encoding="utf-8") as f:
|
|
60
|
-
for m in meta:
|
|
61
|
-
f.write(json.dumps(m, ensure_ascii=False) + "\n")
|
|
62
|
-
|
|
63
|
-
return index, meta
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def load_index(index_paths: IndexPaths) -> Tuple[faiss.Index, List[dict]]:
|
|
67
|
-
index = faiss.read_index(str(index_paths.index_file))
|
|
68
|
-
meta: List[dict] = []
|
|
69
|
-
with index_paths.meta_file.open("r", encoding="utf-8") as f:
|
|
70
|
-
for line in f:
|
|
71
|
-
meta.append(json.loads(line))
|
|
72
|
-
return index, meta
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def ensure_index(embedder: Embedder) -> Tuple[faiss.Index, List[dict]]:
|
|
76
|
-
data_dir = Path(settings.data_dir)
|
|
77
|
-
index_paths = IndexPaths.from_settings()
|
|
78
|
-
if not index_paths.index_file.exists() or not index_paths.meta_file.exists():
|
|
79
|
-
return build_index(data_dir, index_paths, embedder)
|
|
80
|
-
return load_index(index_paths)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def main():
|
|
84
|
-
parser = argparse.ArgumentParser(description="Build or rebuild FAISS index from data/")
|
|
85
|
-
parser.add_argument(
|
|
86
|
-
"--rebuild",
|
|
87
|
-
action="store_true",
|
|
88
|
-
help="Force rebuild index from scratch (ignores existing index files)",
|
|
89
|
-
)
|
|
90
|
-
args = parser.parse_args()
|
|
91
|
-
|
|
92
|
-
emb = Embedder()
|
|
93
|
-
data_dir = Path(settings.data_dir)
|
|
94
|
-
index_paths = IndexPaths.from_settings()
|
|
95
|
-
|
|
96
|
-
if args.rebuild:
|
|
97
|
-
index_paths.base.mkdir(parents=True, exist_ok=True)
|
|
98
|
-
idx, meta = build_index(data_dir, index_paths, emb)
|
|
99
|
-
else:
|
|
100
|
-
idx, meta = ensure_index(emb)
|
|
101
|
-
|
|
102
|
-
print(f"Indexed {len(meta)} chunks → {settings.index_dir}/{settings.index_file}")
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if __name__ == "__main__":
|
|
106
|
-
main()
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import asyncio
|
|
4
|
-
import contextlib
|
|
5
|
-
|
|
6
|
-
import uvicorn
|
|
7
|
-
|
|
8
|
-
from .api import app as fastapi_app
|
|
9
|
-
from .config import settings
|
|
10
|
-
from .mcp_server import run_server as run_mcp
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
async def main_async():
|
|
14
|
-
config = uvicorn.Config(fastapi_app, host=settings.api_host, port=settings.api_port, log_level="info")
|
|
15
|
-
server = uvicorn.Server(config)
|
|
16
|
-
|
|
17
|
-
async def run_api():
|
|
18
|
-
await server.serve()
|
|
19
|
-
|
|
20
|
-
async def run_ws():
|
|
21
|
-
await run_mcp()
|
|
22
|
-
|
|
23
|
-
await asyncio.gather(run_api(), run_ws())
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def main():
|
|
27
|
-
with contextlib.suppress(KeyboardInterrupt):
|
|
28
|
-
asyncio.run(main_async())
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if __name__ == "__main__":
|
|
32
|
-
main()
|
|
33
|
-
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import asyncio
|
|
4
|
-
import json
|
|
5
|
-
from dataclasses import dataclass
|
|
6
|
-
from typing import Any, Awaitable, Callable, Dict
|
|
7
|
-
|
|
8
|
-
import websockets
|
|
9
|
-
|
|
10
|
-
from .config import settings
|
|
11
|
-
from .embeddings import Embedder
|
|
12
|
-
from .indexer import ensure_index
|
|
13
|
-
from .search import Searcher
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Json = Dict[str, Any]
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@dataclass
|
|
20
|
-
class McpServer:
|
|
21
|
-
host: str = settings.mcp_host
|
|
22
|
-
port: int = settings.mcp_port
|
|
23
|
-
|
|
24
|
-
def __post_init__(self):
|
|
25
|
-
emb = Embedder()
|
|
26
|
-
index, meta = ensure_index(emb)
|
|
27
|
-
self.searcher = Searcher(index=index, meta=meta, embedder=emb)
|
|
28
|
-
|
|
29
|
-
async def handler(self, websocket):
|
|
30
|
-
async for message in websocket:
|
|
31
|
-
try:
|
|
32
|
-
req = json.loads(message)
|
|
33
|
-
if not isinstance(req, dict):
|
|
34
|
-
raise ValueError("Invalid JSON-RPC request")
|
|
35
|
-
method = req.get("method")
|
|
36
|
-
req_id = req.get("id")
|
|
37
|
-
params = req.get("params", {})
|
|
38
|
-
|
|
39
|
-
if method == "vw.search":
|
|
40
|
-
q = params.get("query")
|
|
41
|
-
k = int(params.get("k", 6))
|
|
42
|
-
result = self.searcher.search(q, k=k)
|
|
43
|
-
elif method == "vw.answer":
|
|
44
|
-
q = params.get("query")
|
|
45
|
-
k = int(params.get("k", 6))
|
|
46
|
-
result = self.searcher.answer(q, k=k)
|
|
47
|
-
elif method == "vw.get":
|
|
48
|
-
doc_id = params.get("doc_id")
|
|
49
|
-
chunk_id = int(params.get("chunk_id"))
|
|
50
|
-
result = self.searcher.get(doc_id, chunk_id)
|
|
51
|
-
else:
|
|
52
|
-
raise ValueError(f"Unknown method: {method}")
|
|
53
|
-
|
|
54
|
-
resp: Json = {"jsonrpc": "2.0", "id": req_id, "result": result}
|
|
55
|
-
except Exception as e: # pragma: no cover - basic error surface
|
|
56
|
-
resp = {
|
|
57
|
-
"jsonrpc": "2.0",
|
|
58
|
-
"id": req.get("id") if isinstance(req, dict) else None,
|
|
59
|
-
"error": {"code": -32000, "message": str(e)},
|
|
60
|
-
}
|
|
61
|
-
await websocket.send(json.dumps(resp, ensure_ascii=False))
|
|
62
|
-
|
|
63
|
-
async def run(self):
|
|
64
|
-
async with websockets.serve(self.handler, self.host, self.port):
|
|
65
|
-
await asyncio.Future() # run forever
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
async def run_server():
|
|
69
|
-
server = McpServer()
|
|
70
|
-
await server.run()
|
|
71
|
-
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
from typing import List, Tuple
|
|
5
|
-
|
|
6
|
-
import faiss # type: ignore
|
|
7
|
-
import numpy as np
|
|
8
|
-
|
|
9
|
-
from .embeddings import Embedder
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@dataclass
|
|
13
|
-
class Searcher:
|
|
14
|
-
index: faiss.Index
|
|
15
|
-
meta: List[dict]
|
|
16
|
-
embedder: Embedder
|
|
17
|
-
|
|
18
|
-
def search(self, query: str, k: int = 6) -> List[dict]:
|
|
19
|
-
vec = self.embedder.encode([query]) # normalized
|
|
20
|
-
scores, idxs = self.index.search(vec, k)
|
|
21
|
-
out: List[dict] = []
|
|
22
|
-
for score, idx in zip(scores[0].tolist(), idxs[0].tolist()):
|
|
23
|
-
if idx == -1:
|
|
24
|
-
continue
|
|
25
|
-
m = self.meta[idx]
|
|
26
|
-
out.append({
|
|
27
|
-
"score": float(score),
|
|
28
|
-
"doc_id": m["doc_id"],
|
|
29
|
-
"chunk_id": m["chunk_id"],
|
|
30
|
-
"text": m["text"],
|
|
31
|
-
})
|
|
32
|
-
return out
|
|
33
|
-
|
|
34
|
-
def get(self, doc_id: str, chunk_id: int) -> dict:
|
|
35
|
-
# naive lookup by scanning meta (meta is same order as index)
|
|
36
|
-
for m in self.meta:
|
|
37
|
-
if m["doc_id"] == doc_id and m["chunk_id"] == chunk_id:
|
|
38
|
-
return m
|
|
39
|
-
raise KeyError(f"Chunk not found: {doc_id}#{chunk_id}")
|
|
40
|
-
|
|
41
|
-
def answer(self, query: str, k: int = 6) -> dict:
|
|
42
|
-
# Simple extractive draft: stitch top-k snippets
|
|
43
|
-
hits = self.search(query, k=k)
|
|
44
|
-
summary_lines: List[str] = []
|
|
45
|
-
for i, h in enumerate(hits, start=1):
|
|
46
|
-
text = h["text"].strip().splitlines()
|
|
47
|
-
lead = " ".join(text[:2])[:400]
|
|
48
|
-
summary_lines.append(f"[{i}] {lead}")
|
|
49
|
-
draft = (
|
|
50
|
-
"以下は関連する資料からの抜粋です。詳細は出典を参照してください。\n\n"
|
|
51
|
-
+ "\n".join(summary_lines)
|
|
52
|
-
)
|
|
53
|
-
return {"draft": draft, "sources": hits}
|
|
54
|
-
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import hashlib
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def iter_text_files(root: Path):
|
|
6
|
-
exts = {".md", ".markdown", ".html", ".htm", ".txt"}
|
|
7
|
-
for p in sorted(root.rglob("*")):
|
|
8
|
-
if p.is_file() and p.suffix.lower() in exts:
|
|
9
|
-
yield p
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def stable_uid(text: str) -> str:
|
|
13
|
-
return hashlib.md5(text.encode("utf-8")).hexdigest() # nosec - non-crypto id
|
|
14
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|