@techwavedev/agi-agent-kit 1.1.3
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 +59 -0
- package/README.md +147 -0
- package/bin/init.js +471 -0
- package/package.json +36 -0
- package/templates/.agent/agents/backend-specialist.md +263 -0
- package/templates/.agent/agents/code-archaeologist.md +106 -0
- package/templates/.agent/agents/database-architect.md +226 -0
- package/templates/.agent/agents/debugger.md +225 -0
- package/templates/.agent/agents/devops-engineer.md +242 -0
- package/templates/.agent/agents/documentation-writer.md +104 -0
- package/templates/.agent/agents/explorer-agent.md +73 -0
- package/templates/.agent/agents/frontend-specialist.md +556 -0
- package/templates/.agent/agents/game-developer.md +162 -0
- package/templates/.agent/agents/mobile-developer.md +377 -0
- package/templates/.agent/agents/orchestrator.md +416 -0
- package/templates/.agent/agents/penetration-tester.md +188 -0
- package/templates/.agent/agents/performance-optimizer.md +187 -0
- package/templates/.agent/agents/product-manager.md +112 -0
- package/templates/.agent/agents/project-planner.md +403 -0
- package/templates/.agent/agents/qa-automation-engineer.md +109 -0
- package/templates/.agent/agents/security-auditor.md +170 -0
- package/templates/.agent/agents/seo-specialist.md +111 -0
- package/templates/.agent/agents/test-engineer.md +158 -0
- package/templates/.agent/rules/GEMINI.md +253 -0
- package/templates/.agent/workflows/brainstorm.md +113 -0
- package/templates/.agent/workflows/create.md +59 -0
- package/templates/.agent/workflows/debug.md +103 -0
- package/templates/.agent/workflows/deploy.md +176 -0
- package/templates/.agent/workflows/enhance.md +63 -0
- package/templates/.agent/workflows/orchestrate.md +237 -0
- package/templates/.agent/workflows/plan.md +89 -0
- package/templates/.agent/workflows/preview.md +81 -0
- package/templates/.agent/workflows/status.md +86 -0
- package/templates/.agent/workflows/test.md +144 -0
- package/templates/.agent/workflows/ui-ux-pro-max.md +296 -0
- package/templates/base/.env.example +54 -0
- package/templates/base/AGENTS.md +463 -0
- package/templates/base/requirements.txt +6 -0
- package/templates/base/skill-creator/LICENSE.txt +202 -0
- package/templates/base/skill-creator/SKILL_skillcreator.md +389 -0
- package/templates/base/skill-creator/references/output-patterns.md +82 -0
- package/templates/base/skill-creator/references/workflows.md +28 -0
- package/templates/base/skill-creator/scripts/init_skill.py +304 -0
- package/templates/base/skill-creator/scripts/package_skill.py +110 -0
- package/templates/base/skill-creator/scripts/quick_validate.py +95 -0
- package/templates/base/skill-creator/scripts/update_catalog.py +371 -0
- package/templates/skills/core/README.md +21 -0
- package/templates/skills/core/documentation/SKILL.md +351 -0
- package/templates/skills/core/documentation/references/best_practices.md +201 -0
- package/templates/skills/core/documentation/scripts/analyze_code.py +307 -0
- package/templates/skills/core/documentation/scripts/detect_changes.py +460 -0
- package/templates/skills/core/documentation/scripts/generate_changelog.py +312 -0
- package/templates/skills/core/documentation/scripts/sync_docs.py +272 -0
- package/templates/skills/core/documentation/scripts/update_skill_docs.py +366 -0
- package/templates/skills/core/pdf-reader/SKILL.md +104 -0
- package/templates/skills/core/pdf-reader/references/pdf_libraries.md +83 -0
- package/templates/skills/core/pdf-reader/scripts/extract_text.py +295 -0
- package/templates/skills/core/qdrant-memory/SKILL.md +435 -0
- package/templates/skills/core/qdrant-memory/references/advanced_patterns.md +375 -0
- package/templates/skills/core/qdrant-memory/references/collection_schemas.md +229 -0
- package/templates/skills/core/qdrant-memory/references/complete_guide.md +724 -0
- package/templates/skills/core/qdrant-memory/references/embedding_models.md +325 -0
- package/templates/skills/core/qdrant-memory/scripts/benchmark_token_savings.py +640 -0
- package/templates/skills/core/qdrant-memory/scripts/embedding_utils.py +323 -0
- package/templates/skills/core/qdrant-memory/scripts/hybrid_search.py +214 -0
- package/templates/skills/core/qdrant-memory/scripts/init_collection.py +193 -0
- package/templates/skills/core/qdrant-memory/scripts/memory_retrieval.py +345 -0
- package/templates/skills/core/qdrant-memory/scripts/semantic_cache.py +282 -0
- package/templates/skills/core/qdrant-memory/scripts/test_skill.py +655 -0
- package/templates/skills/core/webcrawler/SKILL.md +292 -0
- package/templates/skills/core/webcrawler/references/advanced_crawling.md +181 -0
- package/templates/skills/core/webcrawler/scripts/crawl_docs.py +532 -0
- package/templates/skills/core/webcrawler/scripts/extract_page.py +189 -0
- package/templates/skills/core/webcrawler/scripts/filter_docs.py +200 -0
- package/templates/skills/knowledge/api-patterns/SKILL.md +81 -0
- package/templates/skills/knowledge/api-patterns/api-style.md +42 -0
- package/templates/skills/knowledge/api-patterns/auth.md +24 -0
- package/templates/skills/knowledge/api-patterns/documentation.md +26 -0
- package/templates/skills/knowledge/api-patterns/graphql.md +41 -0
- package/templates/skills/knowledge/api-patterns/rate-limiting.md +31 -0
- package/templates/skills/knowledge/api-patterns/response.md +37 -0
- package/templates/skills/knowledge/api-patterns/rest.md +40 -0
- package/templates/skills/knowledge/api-patterns/scripts/api_validator.py +211 -0
- package/templates/skills/knowledge/api-patterns/security-testing.md +122 -0
- package/templates/skills/knowledge/api-patterns/trpc.md +41 -0
- package/templates/skills/knowledge/api-patterns/versioning.md +22 -0
- package/templates/skills/knowledge/app-builder/SKILL.md +75 -0
- package/templates/skills/knowledge/app-builder/agent-coordination.md +71 -0
- package/templates/skills/knowledge/app-builder/feature-building.md +53 -0
- package/templates/skills/knowledge/app-builder/project-detection.md +34 -0
- package/templates/skills/knowledge/app-builder/scaffolding.md +118 -0
- package/templates/skills/knowledge/app-builder/tech-stack.md +40 -0
- package/templates/skills/knowledge/app-builder/templates/SKILL.md +39 -0
- package/templates/skills/knowledge/app-builder/templates/astro-static/TEMPLATE.md +76 -0
- package/templates/skills/knowledge/app-builder/templates/chrome-extension/TEMPLATE.md +92 -0
- package/templates/skills/knowledge/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
- package/templates/skills/knowledge/app-builder/templates/electron-desktop/TEMPLATE.md +88 -0
- package/templates/skills/knowledge/app-builder/templates/express-api/TEMPLATE.md +83 -0
- package/templates/skills/knowledge/app-builder/templates/flutter-app/TEMPLATE.md +90 -0
- package/templates/skills/knowledge/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -0
- package/templates/skills/knowledge/app-builder/templates/nextjs-fullstack/TEMPLATE.md +82 -0
- package/templates/skills/knowledge/app-builder/templates/nextjs-saas/TEMPLATE.md +100 -0
- package/templates/skills/knowledge/app-builder/templates/nextjs-static/TEMPLATE.md +106 -0
- package/templates/skills/knowledge/app-builder/templates/nuxt-app/TEMPLATE.md +101 -0
- package/templates/skills/knowledge/app-builder/templates/python-fastapi/TEMPLATE.md +83 -0
- package/templates/skills/knowledge/app-builder/templates/react-native-app/TEMPLATE.md +93 -0
- package/templates/skills/knowledge/architecture/SKILL.md +55 -0
- package/templates/skills/knowledge/architecture/context-discovery.md +43 -0
- package/templates/skills/knowledge/architecture/examples.md +94 -0
- package/templates/skills/knowledge/architecture/pattern-selection.md +68 -0
- package/templates/skills/knowledge/architecture/patterns-reference.md +50 -0
- package/templates/skills/knowledge/architecture/trade-off-analysis.md +77 -0
- package/templates/skills/knowledge/bash-linux/SKILL.md +199 -0
- package/templates/skills/knowledge/behavioral-modes/SKILL.md +242 -0
- package/templates/skills/knowledge/brainstorming/SKILL.md +163 -0
- package/templates/skills/knowledge/brainstorming/dynamic-questioning.md +350 -0
- package/templates/skills/knowledge/clean-code/SKILL.md +201 -0
- package/templates/skills/knowledge/code-review-checklist/SKILL.md +109 -0
- package/templates/skills/knowledge/database-design/SKILL.md +52 -0
- package/templates/skills/knowledge/database-design/database-selection.md +43 -0
- package/templates/skills/knowledge/database-design/indexing.md +39 -0
- package/templates/skills/knowledge/database-design/migrations.md +48 -0
- package/templates/skills/knowledge/database-design/optimization.md +36 -0
- package/templates/skills/knowledge/database-design/orm-selection.md +30 -0
- package/templates/skills/knowledge/database-design/schema-design.md +56 -0
- package/templates/skills/knowledge/database-design/scripts/schema_validator.py +172 -0
- package/templates/skills/knowledge/deployment-procedures/SKILL.md +241 -0
- package/templates/skills/knowledge/doc.md +177 -0
- package/templates/skills/knowledge/documentation-templates/SKILL.md +194 -0
- package/templates/skills/knowledge/frontend-design/SKILL.md +396 -0
- package/templates/skills/knowledge/frontend-design/animation-guide.md +331 -0
- package/templates/skills/knowledge/frontend-design/color-system.md +311 -0
- package/templates/skills/knowledge/frontend-design/decision-trees.md +418 -0
- package/templates/skills/knowledge/frontend-design/motion-graphics.md +306 -0
- package/templates/skills/knowledge/frontend-design/scripts/accessibility_checker.py +183 -0
- package/templates/skills/knowledge/frontend-design/scripts/ux_audit.py +722 -0
- package/templates/skills/knowledge/frontend-design/typography-system.md +345 -0
- package/templates/skills/knowledge/frontend-design/ux-psychology.md +541 -0
- package/templates/skills/knowledge/frontend-design/visual-effects.md +383 -0
- package/templates/skills/knowledge/game-development/2d-games/SKILL.md +119 -0
- package/templates/skills/knowledge/game-development/3d-games/SKILL.md +135 -0
- package/templates/skills/knowledge/game-development/SKILL.md +167 -0
- package/templates/skills/knowledge/game-development/game-art/SKILL.md +185 -0
- package/templates/skills/knowledge/game-development/game-audio/SKILL.md +190 -0
- package/templates/skills/knowledge/game-development/game-design/SKILL.md +129 -0
- package/templates/skills/knowledge/game-development/mobile-games/SKILL.md +108 -0
- package/templates/skills/knowledge/game-development/multiplayer/SKILL.md +132 -0
- package/templates/skills/knowledge/game-development/pc-games/SKILL.md +144 -0
- package/templates/skills/knowledge/game-development/vr-ar/SKILL.md +123 -0
- package/templates/skills/knowledge/game-development/web-games/SKILL.md +150 -0
- package/templates/skills/knowledge/geo-fundamentals/SKILL.md +156 -0
- package/templates/skills/knowledge/geo-fundamentals/scripts/geo_checker.py +289 -0
- package/templates/skills/knowledge/i18n-localization/SKILL.md +154 -0
- package/templates/skills/knowledge/i18n-localization/scripts/i18n_checker.py +241 -0
- package/templates/skills/knowledge/intelligent-routing/SKILL.md +334 -0
- package/templates/skills/knowledge/lint-and-validate/SKILL.md +45 -0
- package/templates/skills/knowledge/lint-and-validate/scripts/lint_runner.py +172 -0
- package/templates/skills/knowledge/lint-and-validate/scripts/type_coverage.py +173 -0
- package/templates/skills/knowledge/mcp-builder/SKILL.md +176 -0
- package/templates/skills/knowledge/mobile-design/SKILL.md +394 -0
- package/templates/skills/knowledge/mobile-design/decision-trees.md +516 -0
- package/templates/skills/knowledge/mobile-design/mobile-backend.md +491 -0
- package/templates/skills/knowledge/mobile-design/mobile-color-system.md +420 -0
- package/templates/skills/knowledge/mobile-design/mobile-debugging.md +122 -0
- package/templates/skills/knowledge/mobile-design/mobile-design-thinking.md +357 -0
- package/templates/skills/knowledge/mobile-design/mobile-navigation.md +458 -0
- package/templates/skills/knowledge/mobile-design/mobile-performance.md +767 -0
- package/templates/skills/knowledge/mobile-design/mobile-testing.md +356 -0
- package/templates/skills/knowledge/mobile-design/mobile-typography.md +433 -0
- package/templates/skills/knowledge/mobile-design/platform-android.md +666 -0
- package/templates/skills/knowledge/mobile-design/platform-ios.md +561 -0
- package/templates/skills/knowledge/mobile-design/scripts/mobile_audit.py +670 -0
- package/templates/skills/knowledge/mobile-design/touch-psychology.md +537 -0
- package/templates/skills/knowledge/nextjs-best-practices/SKILL.md +203 -0
- package/templates/skills/knowledge/nodejs-best-practices/SKILL.md +333 -0
- package/templates/skills/knowledge/parallel-agents/SKILL.md +175 -0
- package/templates/skills/knowledge/performance-profiling/SKILL.md +143 -0
- package/templates/skills/knowledge/performance-profiling/scripts/lighthouse_audit.py +76 -0
- package/templates/skills/knowledge/plan-writing/SKILL.md +152 -0
- package/templates/skills/knowledge/powershell-windows/SKILL.md +167 -0
- package/templates/skills/knowledge/python-patterns/SKILL.md +441 -0
- package/templates/skills/knowledge/react-patterns/SKILL.md +198 -0
- package/templates/skills/knowledge/red-team-tactics/SKILL.md +199 -0
- package/templates/skills/knowledge/seo-fundamentals/SKILL.md +129 -0
- package/templates/skills/knowledge/seo-fundamentals/scripts/seo_checker.py +219 -0
- package/templates/skills/knowledge/server-management/SKILL.md +161 -0
- package/templates/skills/knowledge/systematic-debugging/SKILL.md +109 -0
- package/templates/skills/knowledge/tailwind-patterns/SKILL.md +269 -0
- package/templates/skills/knowledge/tdd-workflow/SKILL.md +149 -0
- package/templates/skills/knowledge/testing-patterns/SKILL.md +178 -0
- package/templates/skills/knowledge/testing-patterns/scripts/test_runner.py +219 -0
- package/templates/skills/knowledge/vulnerability-scanner/SKILL.md +276 -0
- package/templates/skills/knowledge/vulnerability-scanner/checklists.md +121 -0
- package/templates/skills/knowledge/vulnerability-scanner/scripts/security_scan.py +458 -0
- package/templates/skills/knowledge/webapp-testing/SKILL.md +187 -0
- package/templates/skills/knowledge/webapp-testing/scripts/playwright_runner.py +173 -0
|
@@ -0,0 +1,655 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Script: test_skill.py
|
|
4
|
+
Purpose: Comprehensive test suite for the qdrant-memory skill.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
# Quick connectivity test (no embeddings)
|
|
8
|
+
python test_skill.py --mode connectivity
|
|
9
|
+
|
|
10
|
+
# Full test with LOCAL embeddings (Ollama - recommended for M3 Mac)
|
|
11
|
+
python test_skill.py --mode full --embeddings ollama
|
|
12
|
+
|
|
13
|
+
# Full test with OpenAI embeddings
|
|
14
|
+
python test_skill.py --mode full --embeddings openai
|
|
15
|
+
|
|
16
|
+
# Cleanup after tests
|
|
17
|
+
python test_skill.py --cleanup
|
|
18
|
+
|
|
19
|
+
Prerequisites for Ollama (local):
|
|
20
|
+
1. Install: brew install ollama
|
|
21
|
+
2. Start: ollama serve
|
|
22
|
+
3. Pull model: ollama pull nomic-embed-text
|
|
23
|
+
|
|
24
|
+
Exit Codes:
|
|
25
|
+
0 - All tests passed
|
|
26
|
+
1 - Some tests failed
|
|
27
|
+
2 - Connection error
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
import argparse
|
|
31
|
+
import json
|
|
32
|
+
import os
|
|
33
|
+
import sys
|
|
34
|
+
import uuid
|
|
35
|
+
from datetime import datetime, timezone
|
|
36
|
+
from urllib.request import Request, urlopen
|
|
37
|
+
from urllib.error import URLError, HTTPError
|
|
38
|
+
from typing import Dict, Any, Optional, List
|
|
39
|
+
|
|
40
|
+
# Configuration
|
|
41
|
+
QDRANT_URL = os.environ.get("QDRANT_URL", "http://localhost:6333")
|
|
42
|
+
OLLAMA_URL = os.environ.get("OLLAMA_URL", "http://localhost:11434")
|
|
43
|
+
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "")
|
|
44
|
+
TEST_COLLECTION = "test_qdrant_memory_skill"
|
|
45
|
+
|
|
46
|
+
# Embedding model configurations
|
|
47
|
+
EMBEDDING_CONFIGS = {
|
|
48
|
+
"ollama": {
|
|
49
|
+
"model": "nomic-embed-text",
|
|
50
|
+
"dimensions": 768,
|
|
51
|
+
"url": f"{OLLAMA_URL}/api/embeddings"
|
|
52
|
+
},
|
|
53
|
+
"openai": {
|
|
54
|
+
"model": "text-embedding-3-small",
|
|
55
|
+
"dimensions": 1536,
|
|
56
|
+
"url": "https://api.openai.com/v1/embeddings"
|
|
57
|
+
},
|
|
58
|
+
"bedrock": {
|
|
59
|
+
"model": "amazon.titan-embed-text-v2:0",
|
|
60
|
+
"dimensions": 1024,
|
|
61
|
+
"url": "via-boto3"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# Will be set based on --embeddings argument
|
|
66
|
+
EMBEDDING_PROVIDER = "ollama"
|
|
67
|
+
VECTOR_DIM = 768
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class Colors:
|
|
71
|
+
GREEN = "\033[92m"
|
|
72
|
+
RED = "\033[91m"
|
|
73
|
+
YELLOW = "\033[93m"
|
|
74
|
+
BLUE = "\033[94m"
|
|
75
|
+
CYAN = "\033[96m"
|
|
76
|
+
RESET = "\033[0m"
|
|
77
|
+
BOLD = "\033[1m"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def print_header(text: str):
|
|
81
|
+
print(f"\n{Colors.BOLD}{Colors.BLUE}{'='*60}{Colors.RESET}")
|
|
82
|
+
print(f"{Colors.BOLD}{Colors.BLUE}{text}{Colors.RESET}")
|
|
83
|
+
print(f"{Colors.BOLD}{Colors.BLUE}{'='*60}{Colors.RESET}\n")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def print_test(name: str, passed: bool, details: str = ""):
|
|
87
|
+
icon = f"{Colors.GREEN}โ{Colors.RESET}" if passed else f"{Colors.RED}โ{Colors.RESET}"
|
|
88
|
+
status = f"{Colors.GREEN}PASS{Colors.RESET}" if passed else f"{Colors.RED}FAIL{Colors.RESET}"
|
|
89
|
+
print(f" {icon} {name}: {status}")
|
|
90
|
+
if details:
|
|
91
|
+
print(f" {Colors.YELLOW}{details}{Colors.RESET}")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def qdrant_request(method: str, endpoint: str, data: Optional[Dict] = None) -> Dict:
|
|
95
|
+
"""Make a request to Qdrant API."""
|
|
96
|
+
url = f"{QDRANT_URL}{endpoint}"
|
|
97
|
+
req = Request(
|
|
98
|
+
url,
|
|
99
|
+
data=json.dumps(data).encode() if data else None,
|
|
100
|
+
headers={"Content-Type": "application/json"},
|
|
101
|
+
method=method
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
with urlopen(req, timeout=30) as response:
|
|
105
|
+
return json.loads(response.read().decode())
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def get_embedding_ollama(text: str) -> List[float]:
|
|
109
|
+
"""Generate embedding using Ollama (local)."""
|
|
110
|
+
config = EMBEDDING_CONFIGS["ollama"]
|
|
111
|
+
|
|
112
|
+
payload = {
|
|
113
|
+
"model": config["model"],
|
|
114
|
+
"prompt": text
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
req = Request(
|
|
118
|
+
config["url"],
|
|
119
|
+
data=json.dumps(payload).encode(),
|
|
120
|
+
headers={"Content-Type": "application/json"},
|
|
121
|
+
method="POST"
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
with urlopen(req, timeout=60) as response:
|
|
125
|
+
result = json.loads(response.read().decode())
|
|
126
|
+
return result["embedding"]
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def get_embedding_openai(text: str) -> List[float]:
|
|
130
|
+
"""Generate embedding using OpenAI API."""
|
|
131
|
+
if not OPENAI_API_KEY:
|
|
132
|
+
raise ValueError("OPENAI_API_KEY not set")
|
|
133
|
+
|
|
134
|
+
config = EMBEDDING_CONFIGS["openai"]
|
|
135
|
+
|
|
136
|
+
payload = {
|
|
137
|
+
"input": text,
|
|
138
|
+
"model": config["model"]
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
req = Request(
|
|
142
|
+
config["url"],
|
|
143
|
+
data=json.dumps(payload).encode(),
|
|
144
|
+
headers={
|
|
145
|
+
"Content-Type": "application/json",
|
|
146
|
+
"Authorization": f"Bearer {OPENAI_API_KEY}"
|
|
147
|
+
},
|
|
148
|
+
method="POST"
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
with urlopen(req, timeout=30) as response:
|
|
152
|
+
result = json.loads(response.read().decode())
|
|
153
|
+
return result["data"][0]["embedding"]
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def get_embedding_bedrock(text: str) -> List[float]:
|
|
157
|
+
"""Generate embedding using Amazon Bedrock."""
|
|
158
|
+
try:
|
|
159
|
+
import boto3
|
|
160
|
+
except ImportError:
|
|
161
|
+
raise ImportError("boto3 required for Bedrock: pip install boto3")
|
|
162
|
+
|
|
163
|
+
config = EMBEDDING_CONFIGS["bedrock"]
|
|
164
|
+
session = boto3.Session(region_name=os.environ.get("AWS_REGION", "eu-west-1"))
|
|
165
|
+
bedrock = session.client("bedrock-runtime")
|
|
166
|
+
|
|
167
|
+
body = json.dumps({"inputText": text})
|
|
168
|
+
response = bedrock.invoke_model(
|
|
169
|
+
modelId=config["model"],
|
|
170
|
+
body=body,
|
|
171
|
+
contentType="application/json",
|
|
172
|
+
accept="application/json"
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
result = json.loads(response["body"].read())
|
|
176
|
+
return result["embedding"]
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def get_embedding(text: str) -> List[float]:
|
|
180
|
+
"""Generate embedding using configured provider."""
|
|
181
|
+
if EMBEDDING_PROVIDER == "ollama":
|
|
182
|
+
return get_embedding_ollama(text)
|
|
183
|
+
elif EMBEDDING_PROVIDER == "bedrock":
|
|
184
|
+
return get_embedding_bedrock(text)
|
|
185
|
+
else:
|
|
186
|
+
return get_embedding_openai(text)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def test_connectivity() -> bool:
|
|
190
|
+
"""Test 1: Basic Qdrant connectivity."""
|
|
191
|
+
try:
|
|
192
|
+
result = qdrant_request("GET", "/collections")
|
|
193
|
+
return result.get("status") == "ok"
|
|
194
|
+
except Exception as e:
|
|
195
|
+
print(f" Error: {e}")
|
|
196
|
+
return False
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def test_ollama_connectivity() -> bool:
|
|
200
|
+
"""Test Ollama connectivity."""
|
|
201
|
+
try:
|
|
202
|
+
req = Request(f"{OLLAMA_URL}/api/tags", method="GET")
|
|
203
|
+
with urlopen(req, timeout=10) as response:
|
|
204
|
+
result = json.loads(response.read().decode())
|
|
205
|
+
return "models" in result
|
|
206
|
+
except Exception as e:
|
|
207
|
+
print(f" Error: {e}")
|
|
208
|
+
return False
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def test_create_collection() -> bool:
|
|
212
|
+
"""Test 2: Create test collection."""
|
|
213
|
+
try:
|
|
214
|
+
payload = {
|
|
215
|
+
"vectors": {
|
|
216
|
+
"size": VECTOR_DIM,
|
|
217
|
+
"distance": "Cosine"
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
result = qdrant_request("PUT", f"/collections/{TEST_COLLECTION}", payload)
|
|
221
|
+
return result.get("result") == True or "already exists" in str(result)
|
|
222
|
+
except HTTPError as e:
|
|
223
|
+
if e.code == 409: # Collection exists
|
|
224
|
+
return True
|
|
225
|
+
print(f" Error: {e}")
|
|
226
|
+
return False
|
|
227
|
+
except Exception as e:
|
|
228
|
+
print(f" Error: {e}")
|
|
229
|
+
return False
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def test_collection_info() -> bool:
|
|
233
|
+
"""Test 3: Get collection info."""
|
|
234
|
+
try:
|
|
235
|
+
result = qdrant_request("GET", f"/collections/{TEST_COLLECTION}")
|
|
236
|
+
info = result.get("result", {})
|
|
237
|
+
# Accept both green (optimized) and yellow (indexing) status
|
|
238
|
+
valid_status = info.get("status") in ["green", "yellow"]
|
|
239
|
+
has_points = info.get("points_count") is not None
|
|
240
|
+
return valid_status and has_points
|
|
241
|
+
except Exception as e:
|
|
242
|
+
print(f" Error: {e}")
|
|
243
|
+
return False
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def test_upsert_with_mock_vector() -> bool:
|
|
247
|
+
"""Test 4: Upsert a point with mock vector (no API key needed)."""
|
|
248
|
+
try:
|
|
249
|
+
# Create a mock vector
|
|
250
|
+
mock_vector = [0.1 * (i % 10) for i in range(VECTOR_DIM)]
|
|
251
|
+
|
|
252
|
+
point_id = str(uuid.uuid4())
|
|
253
|
+
payload = {
|
|
254
|
+
"points": [{
|
|
255
|
+
"id": point_id,
|
|
256
|
+
"vector": mock_vector,
|
|
257
|
+
"payload": {
|
|
258
|
+
"content": "This is a test memory for validation purposes",
|
|
259
|
+
"type": "test",
|
|
260
|
+
"project": "qdrant-memory-test",
|
|
261
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
262
|
+
"tags": ["test", "validation"]
|
|
263
|
+
}
|
|
264
|
+
}]
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
result = qdrant_request("PUT", f"/collections/{TEST_COLLECTION}/points?wait=true", payload)
|
|
268
|
+
return result.get("status") == "ok"
|
|
269
|
+
except Exception as e:
|
|
270
|
+
print(f" Error: {e}")
|
|
271
|
+
return False
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def test_search_with_mock_vector() -> bool:
|
|
275
|
+
"""Test 5: Search using mock vector."""
|
|
276
|
+
try:
|
|
277
|
+
mock_vector = [0.1 * (i % 10) for i in range(VECTOR_DIM)]
|
|
278
|
+
|
|
279
|
+
payload = {
|
|
280
|
+
"vector": mock_vector,
|
|
281
|
+
"limit": 5,
|
|
282
|
+
"with_payload": True
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
result = qdrant_request("POST", f"/collections/{TEST_COLLECTION}/points/search", payload)
|
|
286
|
+
results = result.get("result", [])
|
|
287
|
+
return len(results) > 0 and results[0].get("score", 0) > 0.9
|
|
288
|
+
except Exception as e:
|
|
289
|
+
print(f" Error: {e}")
|
|
290
|
+
return False
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def test_filter_search() -> bool:
|
|
294
|
+
"""Test 6: Search with payload filter."""
|
|
295
|
+
try:
|
|
296
|
+
mock_vector = [0.1 * (i % 10) for i in range(VECTOR_DIM)]
|
|
297
|
+
|
|
298
|
+
payload = {
|
|
299
|
+
"vector": mock_vector,
|
|
300
|
+
"limit": 5,
|
|
301
|
+
"with_payload": True,
|
|
302
|
+
"filter": {
|
|
303
|
+
"must": [
|
|
304
|
+
{"key": "type", "match": {"value": "test"}}
|
|
305
|
+
]
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
result = qdrant_request("POST", f"/collections/{TEST_COLLECTION}/points/search", payload)
|
|
310
|
+
results = result.get("result", [])
|
|
311
|
+
return len(results) > 0
|
|
312
|
+
except Exception as e:
|
|
313
|
+
print(f" Error: {e}")
|
|
314
|
+
return False
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def test_embedding_generation() -> bool:
|
|
318
|
+
"""Test 7: Embedding generation (local or OpenAI)."""
|
|
319
|
+
try:
|
|
320
|
+
embedding = get_embedding("This is a test query for embedding generation")
|
|
321
|
+
return len(embedding) == VECTOR_DIM
|
|
322
|
+
except Exception as e:
|
|
323
|
+
print(f" Error: {e}")
|
|
324
|
+
return False
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def test_store_with_real_embedding() -> bool:
|
|
328
|
+
"""Test 8: Store memory with real embedding."""
|
|
329
|
+
try:
|
|
330
|
+
text = "We decided to use PostgreSQL for user data due to ACID compliance requirements"
|
|
331
|
+
embedding = get_embedding(text)
|
|
332
|
+
|
|
333
|
+
point_id = str(uuid.uuid4())
|
|
334
|
+
payload = {
|
|
335
|
+
"points": [{
|
|
336
|
+
"id": point_id,
|
|
337
|
+
"vector": embedding,
|
|
338
|
+
"payload": {
|
|
339
|
+
"content": text,
|
|
340
|
+
"type": "decision",
|
|
341
|
+
"project": "test-project",
|
|
342
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
343
|
+
"tags": ["database", "architecture", "test"]
|
|
344
|
+
}
|
|
345
|
+
}]
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
result = qdrant_request("PUT", f"/collections/{TEST_COLLECTION}/points?wait=true", payload)
|
|
349
|
+
return result.get("status") == "ok"
|
|
350
|
+
except Exception as e:
|
|
351
|
+
print(f" Error: {e}")
|
|
352
|
+
return False
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def test_semantic_search() -> bool:
|
|
356
|
+
"""Test 9: Semantic search (similar but not identical query)."""
|
|
357
|
+
try:
|
|
358
|
+
# Search with semantically similar query
|
|
359
|
+
query = "What database did we choose for storing user information?"
|
|
360
|
+
embedding = get_embedding(query)
|
|
361
|
+
|
|
362
|
+
payload = {
|
|
363
|
+
"vector": embedding,
|
|
364
|
+
"limit": 5,
|
|
365
|
+
"with_payload": True,
|
|
366
|
+
"score_threshold": 0.5 # Lower threshold for local models
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
result = qdrant_request("POST", f"/collections/{TEST_COLLECTION}/points/search", payload)
|
|
370
|
+
results = result.get("result", [])
|
|
371
|
+
|
|
372
|
+
# Check if we found the PostgreSQL decision
|
|
373
|
+
for r in results:
|
|
374
|
+
content = r.get("payload", {}).get("content", "")
|
|
375
|
+
if "PostgreSQL" in content or "database" in content.lower():
|
|
376
|
+
return True
|
|
377
|
+
return len(results) > 0 # At least found something
|
|
378
|
+
except Exception as e:
|
|
379
|
+
print(f" Error: {e}")
|
|
380
|
+
return False
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def test_hybrid_search() -> bool:
|
|
384
|
+
"""Test 10: Hybrid search (vector + filter)."""
|
|
385
|
+
try:
|
|
386
|
+
query = "database architecture"
|
|
387
|
+
embedding = get_embedding(query)
|
|
388
|
+
|
|
389
|
+
payload = {
|
|
390
|
+
"vector": embedding,
|
|
391
|
+
"limit": 5,
|
|
392
|
+
"with_payload": True,
|
|
393
|
+
"filter": {
|
|
394
|
+
"must": [
|
|
395
|
+
{"key": "type", "match": {"value": "decision"}}
|
|
396
|
+
]
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
result = qdrant_request("POST", f"/collections/{TEST_COLLECTION}/points/search", payload)
|
|
401
|
+
results = result.get("result", [])
|
|
402
|
+
|
|
403
|
+
# Verify all results have type=decision
|
|
404
|
+
for r in results:
|
|
405
|
+
if r.get("payload", {}).get("type") != "decision":
|
|
406
|
+
return False
|
|
407
|
+
return len(results) > 0
|
|
408
|
+
except Exception as e:
|
|
409
|
+
print(f" Error: {e}")
|
|
410
|
+
return False
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def test_cache_simulation() -> bool:
|
|
414
|
+
"""Test 11: Cache simulation (store and retrieve similar query)."""
|
|
415
|
+
try:
|
|
416
|
+
# Store a "cached" response
|
|
417
|
+
original_query = "How do I reset my password in the admin panel?"
|
|
418
|
+
response_text = "Navigate to Settings > Security > Reset Password and follow the prompts."
|
|
419
|
+
|
|
420
|
+
embedding = get_embedding(original_query)
|
|
421
|
+
|
|
422
|
+
cache_payload = {
|
|
423
|
+
"points": [{
|
|
424
|
+
"id": str(uuid.uuid4()),
|
|
425
|
+
"vector": embedding,
|
|
426
|
+
"payload": {
|
|
427
|
+
"query": original_query,
|
|
428
|
+
"response": response_text,
|
|
429
|
+
"type": "cache",
|
|
430
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
431
|
+
"model": "test"
|
|
432
|
+
}
|
|
433
|
+
}]
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
qdrant_request("PUT", f"/collections/{TEST_COLLECTION}/points?wait=true", cache_payload)
|
|
437
|
+
|
|
438
|
+
# Now search with a similar (not identical) query
|
|
439
|
+
similar_query = "How can I change my password?"
|
|
440
|
+
similar_embedding = get_embedding(similar_query)
|
|
441
|
+
|
|
442
|
+
search_payload = {
|
|
443
|
+
"vector": similar_embedding,
|
|
444
|
+
"limit": 1,
|
|
445
|
+
"with_payload": True,
|
|
446
|
+
"score_threshold": 0.6, # Adjusted for local models
|
|
447
|
+
"filter": {
|
|
448
|
+
"must": [{"key": "type", "match": {"value": "cache"}}]
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
result = qdrant_request("POST", f"/collections/{TEST_COLLECTION}/points/search", search_payload)
|
|
453
|
+
results = result.get("result", [])
|
|
454
|
+
|
|
455
|
+
if results and results[0].get("score", 0) > 0.6:
|
|
456
|
+
cached_response = results[0].get("payload", {}).get("response")
|
|
457
|
+
return cached_response == response_text
|
|
458
|
+
return False
|
|
459
|
+
except Exception as e:
|
|
460
|
+
print(f" Error: {e}")
|
|
461
|
+
return False
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def cleanup_test_collection() -> bool:
|
|
465
|
+
"""Cleanup: Delete test collection."""
|
|
466
|
+
try:
|
|
467
|
+
result = qdrant_request("DELETE", f"/collections/{TEST_COLLECTION}")
|
|
468
|
+
return result.get("result") == True
|
|
469
|
+
except HTTPError as e:
|
|
470
|
+
if e.code == 404: # Already deleted
|
|
471
|
+
return True
|
|
472
|
+
return False
|
|
473
|
+
except Exception as e:
|
|
474
|
+
print(f" Error: {e}")
|
|
475
|
+
return False
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
def run_connectivity_tests() -> int:
|
|
479
|
+
"""Run basic connectivity tests (no embeddings required)."""
|
|
480
|
+
print_header("๐ CONNECTIVITY TESTS (No Embeddings Required)")
|
|
481
|
+
|
|
482
|
+
tests = [
|
|
483
|
+
("Qdrant Connection", test_connectivity),
|
|
484
|
+
("Create Test Collection", test_create_collection),
|
|
485
|
+
("Get Collection Info", test_collection_info),
|
|
486
|
+
("Upsert with Mock Vector", test_upsert_with_mock_vector),
|
|
487
|
+
("Search with Mock Vector", test_search_with_mock_vector),
|
|
488
|
+
("Filter Search", test_filter_search),
|
|
489
|
+
]
|
|
490
|
+
|
|
491
|
+
passed = 0
|
|
492
|
+
failed = 0
|
|
493
|
+
|
|
494
|
+
for name, test_func in tests:
|
|
495
|
+
try:
|
|
496
|
+
result = test_func()
|
|
497
|
+
print_test(name, result)
|
|
498
|
+
if result:
|
|
499
|
+
passed += 1
|
|
500
|
+
else:
|
|
501
|
+
failed += 1
|
|
502
|
+
except Exception as e:
|
|
503
|
+
print_test(name, False, str(e))
|
|
504
|
+
failed += 1
|
|
505
|
+
|
|
506
|
+
return 0 if failed == 0 else 1
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
def run_full_tests() -> int:
|
|
510
|
+
"""Run full integration tests."""
|
|
511
|
+
global VECTOR_DIM
|
|
512
|
+
|
|
513
|
+
print_header("๐งช FULL INTEGRATION TESTS")
|
|
514
|
+
|
|
515
|
+
config = EMBEDDING_CONFIGS[EMBEDDING_PROVIDER]
|
|
516
|
+
VECTOR_DIM = config["dimensions"]
|
|
517
|
+
|
|
518
|
+
print(f" {Colors.CYAN}Embedding Provider: {EMBEDDING_PROVIDER.upper()}{Colors.RESET}")
|
|
519
|
+
print(f" {Colors.CYAN}Model: {config['model']}{Colors.RESET}")
|
|
520
|
+
print(f" {Colors.CYAN}Dimensions: {VECTOR_DIM}{Colors.RESET}\n")
|
|
521
|
+
|
|
522
|
+
# Check embedding provider is available
|
|
523
|
+
if EMBEDDING_PROVIDER == "ollama":
|
|
524
|
+
if not test_ollama_connectivity():
|
|
525
|
+
print(f"\n{Colors.RED}โ Ollama is not running!{Colors.RESET}")
|
|
526
|
+
print(f"\n{Colors.YELLOW}To start Ollama:{Colors.RESET}")
|
|
527
|
+
print(f" 1. Start server: ollama serve")
|
|
528
|
+
print(f" 2. Pull model: ollama pull nomic-embed-text")
|
|
529
|
+
print(f"\nAlternatively, use OpenAI:")
|
|
530
|
+
print(f" python test_skill.py --mode full --embeddings openai\n")
|
|
531
|
+
return 1
|
|
532
|
+
print_test("Ollama Connection", True)
|
|
533
|
+
elif EMBEDDING_PROVIDER == "openai":
|
|
534
|
+
if not OPENAI_API_KEY:
|
|
535
|
+
print(f"\n{Colors.RED}โ OPENAI_API_KEY not set!{Colors.RESET}")
|
|
536
|
+
print(f"\n{Colors.YELLOW}To use OpenAI, set your API key:{Colors.RESET}")
|
|
537
|
+
print(f" export OPENAI_API_KEY='your-key-here'")
|
|
538
|
+
print(f"\nAlternatively, use Ollama (local, free):")
|
|
539
|
+
print(f" python test_skill.py --mode full --embeddings ollama\n")
|
|
540
|
+
return 1
|
|
541
|
+
|
|
542
|
+
# Run connectivity tests first (with correct vector dimensions)
|
|
543
|
+
conn_result = run_connectivity_tests()
|
|
544
|
+
if conn_result != 0:
|
|
545
|
+
print(f"\n{Colors.RED}Connectivity tests failed. Skipping integration tests.{Colors.RESET}")
|
|
546
|
+
return 1
|
|
547
|
+
|
|
548
|
+
print_header("๐ค EMBEDDING & SEMANTIC TESTS")
|
|
549
|
+
|
|
550
|
+
tests = [
|
|
551
|
+
(f"Embedding Generation ({EMBEDDING_PROVIDER})", test_embedding_generation),
|
|
552
|
+
("Store with Real Embedding", test_store_with_real_embedding),
|
|
553
|
+
("Semantic Search (Similar Query)", test_semantic_search),
|
|
554
|
+
("Hybrid Search (Vector + Filter)", test_hybrid_search),
|
|
555
|
+
("Cache Simulation (Store & Retrieve)", test_cache_simulation),
|
|
556
|
+
]
|
|
557
|
+
|
|
558
|
+
passed = 0
|
|
559
|
+
failed = 0
|
|
560
|
+
|
|
561
|
+
for name, test_func in tests:
|
|
562
|
+
try:
|
|
563
|
+
result = test_func()
|
|
564
|
+
print_test(name, result)
|
|
565
|
+
if result:
|
|
566
|
+
passed += 1
|
|
567
|
+
else:
|
|
568
|
+
failed += 1
|
|
569
|
+
except Exception as e:
|
|
570
|
+
print_test(name, False, str(e))
|
|
571
|
+
failed += 1
|
|
572
|
+
|
|
573
|
+
return 0 if failed == 0 else 1
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
def main():
|
|
577
|
+
global EMBEDDING_PROVIDER, VECTOR_DIM
|
|
578
|
+
|
|
579
|
+
parser = argparse.ArgumentParser(description="Test the qdrant-memory skill")
|
|
580
|
+
parser.add_argument(
|
|
581
|
+
"--mode",
|
|
582
|
+
choices=["connectivity", "full"],
|
|
583
|
+
default="full",
|
|
584
|
+
help="Test mode: connectivity (no embeddings) or full (with embeddings)"
|
|
585
|
+
)
|
|
586
|
+
parser.add_argument(
|
|
587
|
+
"--embeddings",
|
|
588
|
+
choices=["ollama", "openai", "bedrock"],
|
|
589
|
+
default="ollama",
|
|
590
|
+
help="Embedding provider: ollama (local, free), bedrock (AWS), or openai (cloud)"
|
|
591
|
+
)
|
|
592
|
+
parser.add_argument(
|
|
593
|
+
"--cleanup",
|
|
594
|
+
action="store_true",
|
|
595
|
+
help="Delete test collection after tests"
|
|
596
|
+
)
|
|
597
|
+
parser.add_argument(
|
|
598
|
+
"--cleanup-only",
|
|
599
|
+
action="store_true",
|
|
600
|
+
help="Only cleanup (delete test collection)"
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
args = parser.parse_args()
|
|
604
|
+
|
|
605
|
+
EMBEDDING_PROVIDER = args.embeddings
|
|
606
|
+
VECTOR_DIM = EMBEDDING_CONFIGS[EMBEDDING_PROVIDER]["dimensions"]
|
|
607
|
+
|
|
608
|
+
print(f"\n{Colors.BOLD}๐ง Qdrant Memory Skill Test Suite{Colors.RESET}")
|
|
609
|
+
print(f" Qdrant URL: {QDRANT_URL}")
|
|
610
|
+
print(f" Test Collection: {TEST_COLLECTION}")
|
|
611
|
+
print(f" Embedding Provider: {EMBEDDING_PROVIDER.upper()}")
|
|
612
|
+
|
|
613
|
+
if args.cleanup_only:
|
|
614
|
+
print_header("๐งน CLEANUP")
|
|
615
|
+
result = cleanup_test_collection()
|
|
616
|
+
print_test("Delete Test Collection", result)
|
|
617
|
+
sys.exit(0 if result else 1)
|
|
618
|
+
|
|
619
|
+
try:
|
|
620
|
+
# Check Qdrant is reachable
|
|
621
|
+
try:
|
|
622
|
+
qdrant_request("GET", "/collections")
|
|
623
|
+
except URLError as e:
|
|
624
|
+
print(f"\n{Colors.RED}โ Cannot connect to Qdrant at {QDRANT_URL}{Colors.RESET}")
|
|
625
|
+
print(f" Error: {e}")
|
|
626
|
+
print(f"\n{Colors.YELLOW}Make sure Qdrant is running:{Colors.RESET}")
|
|
627
|
+
print(f" docker run -p 6333:6333 qdrant/qdrant")
|
|
628
|
+
sys.exit(2)
|
|
629
|
+
|
|
630
|
+
if args.mode == "connectivity":
|
|
631
|
+
result = run_connectivity_tests()
|
|
632
|
+
else:
|
|
633
|
+
result = run_full_tests()
|
|
634
|
+
|
|
635
|
+
if args.cleanup:
|
|
636
|
+
print_header("๐งน CLEANUP")
|
|
637
|
+
cleanup_result = cleanup_test_collection()
|
|
638
|
+
print_test("Delete Test Collection", cleanup_result)
|
|
639
|
+
|
|
640
|
+
# Final summary
|
|
641
|
+
print_header("๐ TEST SUMMARY")
|
|
642
|
+
if result == 0:
|
|
643
|
+
print(f" {Colors.GREEN}All tests passed! โ{Colors.RESET}")
|
|
644
|
+
else:
|
|
645
|
+
print(f" {Colors.RED}Some tests failed. See details above.{Colors.RESET}")
|
|
646
|
+
|
|
647
|
+
sys.exit(result)
|
|
648
|
+
|
|
649
|
+
except KeyboardInterrupt:
|
|
650
|
+
print(f"\n{Colors.YELLOW}Tests interrupted.{Colors.RESET}")
|
|
651
|
+
sys.exit(1)
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
if __name__ == "__main__":
|
|
655
|
+
main()
|