@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,323 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Shared embedding utilities for the qdrant-memory skill.
|
|
4
|
+
Supports Ollama (local), OpenAI (cloud), and Amazon Bedrock (AWS) embeddings.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
from embedding_utils import get_embedding, get_embedding_dimension
|
|
8
|
+
|
|
9
|
+
embedding = get_embedding("Your text here")
|
|
10
|
+
dim = get_embedding_dimension()
|
|
11
|
+
|
|
12
|
+
Environment Variables:
|
|
13
|
+
EMBEDDING_PROVIDER - "ollama" (default), "openai", or "bedrock"
|
|
14
|
+
|
|
15
|
+
# Ollama (local/private)
|
|
16
|
+
OLLAMA_URL - Ollama server URL (default: http://localhost:11434)
|
|
17
|
+
|
|
18
|
+
# OpenAI (cloud)
|
|
19
|
+
OPENAI_API_KEY - Required for OpenAI provider
|
|
20
|
+
|
|
21
|
+
# Amazon Bedrock (AWS - uses your Kiro subscription)
|
|
22
|
+
AWS_PROFILE - AWS profile name (default: uses default profile)
|
|
23
|
+
AWS_REGION - AWS region (default: eu-west-1)
|
|
24
|
+
|
|
25
|
+
EMBEDDING_MODEL - Model name (defaults based on provider)
|
|
26
|
+
|
|
27
|
+
Pricing Comparison:
|
|
28
|
+
- Ollama: FREE (local)
|
|
29
|
+
- Bedrock Titan Embed V2: ~$0.02/1M tokens (cheapest cloud)
|
|
30
|
+
- OpenAI text-embedding-3-small: ~$0.02/1M tokens
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
import json
|
|
34
|
+
import os
|
|
35
|
+
from typing import List
|
|
36
|
+
from urllib.request import Request, urlopen
|
|
37
|
+
from urllib.error import URLError
|
|
38
|
+
|
|
39
|
+
# Configuration with Ollama as default (local/private)
|
|
40
|
+
EMBEDDING_PROVIDER = os.environ.get("EMBEDDING_PROVIDER", "ollama")
|
|
41
|
+
OLLAMA_URL = os.environ.get("OLLAMA_URL", "http://localhost:11434")
|
|
42
|
+
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "")
|
|
43
|
+
AWS_REGION = os.environ.get("AWS_REGION", "eu-west-1")
|
|
44
|
+
|
|
45
|
+
# Model configurations
|
|
46
|
+
PROVIDER_CONFIGS = {
|
|
47
|
+
"ollama": {
|
|
48
|
+
"default_model": "nomic-embed-text",
|
|
49
|
+
"dimensions": {
|
|
50
|
+
"nomic-embed-text": 768,
|
|
51
|
+
"mxbai-embed-large": 1024,
|
|
52
|
+
"all-minilm": 384,
|
|
53
|
+
},
|
|
54
|
+
"default_dim": 768
|
|
55
|
+
},
|
|
56
|
+
"openai": {
|
|
57
|
+
"default_model": "text-embedding-3-small",
|
|
58
|
+
"dimensions": {
|
|
59
|
+
"text-embedding-3-small": 1536,
|
|
60
|
+
"text-embedding-3-large": 3072,
|
|
61
|
+
"text-embedding-ada-002": 1536,
|
|
62
|
+
},
|
|
63
|
+
"default_dim": 1536
|
|
64
|
+
},
|
|
65
|
+
"bedrock": {
|
|
66
|
+
"default_model": "amazon.titan-embed-text-v2:0",
|
|
67
|
+
"dimensions": {
|
|
68
|
+
"amazon.titan-embed-text-v2:0": 1024,
|
|
69
|
+
"amazon.titan-embed-text-v1": 1536,
|
|
70
|
+
"cohere.embed-english-v3": 1024,
|
|
71
|
+
"cohere.embed-multilingual-v3": 1024,
|
|
72
|
+
},
|
|
73
|
+
"default_dim": 1024
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
EMBEDDING_MODEL = os.environ.get(
|
|
78
|
+
"EMBEDDING_MODEL",
|
|
79
|
+
PROVIDER_CONFIGS.get(EMBEDDING_PROVIDER, {}).get("default_model", "nomic-embed-text")
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def get_embedding_dimension() -> int:
|
|
84
|
+
"""Get the dimension for the current embedding configuration."""
|
|
85
|
+
config = PROVIDER_CONFIGS.get(EMBEDDING_PROVIDER, PROVIDER_CONFIGS["ollama"])
|
|
86
|
+
return config["dimensions"].get(EMBEDDING_MODEL, config["default_dim"])
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def get_embedding_ollama(text: str) -> List[float]:
|
|
90
|
+
"""Generate embedding using Ollama (local)."""
|
|
91
|
+
payload = {
|
|
92
|
+
"model": EMBEDDING_MODEL,
|
|
93
|
+
"prompt": text
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
req = Request(
|
|
97
|
+
f"{OLLAMA_URL}/api/embeddings",
|
|
98
|
+
data=json.dumps(payload).encode(),
|
|
99
|
+
headers={"Content-Type": "application/json"},
|
|
100
|
+
method="POST"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
with urlopen(req, timeout=60) as response:
|
|
104
|
+
result = json.loads(response.read().decode())
|
|
105
|
+
return result["embedding"]
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def get_embedding_openai(text: str) -> List[float]:
|
|
109
|
+
"""Generate embedding using OpenAI API."""
|
|
110
|
+
if not OPENAI_API_KEY:
|
|
111
|
+
raise ValueError("OPENAI_API_KEY environment variable not set")
|
|
112
|
+
|
|
113
|
+
payload = {
|
|
114
|
+
"input": text,
|
|
115
|
+
"model": EMBEDDING_MODEL
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
req = Request(
|
|
119
|
+
"https://api.openai.com/v1/embeddings",
|
|
120
|
+
data=json.dumps(payload).encode(),
|
|
121
|
+
headers={
|
|
122
|
+
"Content-Type": "application/json",
|
|
123
|
+
"Authorization": f"Bearer {OPENAI_API_KEY}"
|
|
124
|
+
},
|
|
125
|
+
method="POST"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
with urlopen(req, timeout=30) as response:
|
|
129
|
+
result = json.loads(response.read().decode())
|
|
130
|
+
return result["data"][0]["embedding"]
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def get_embedding_bedrock(text: str) -> List[float]:
|
|
134
|
+
"""
|
|
135
|
+
Generate embedding using Amazon Bedrock.
|
|
136
|
+
|
|
137
|
+
Uses boto3 with AWS credentials from:
|
|
138
|
+
- AWS_PROFILE environment variable
|
|
139
|
+
- ~/.aws/credentials (default profile)
|
|
140
|
+
- IAM role (if running on AWS)
|
|
141
|
+
|
|
142
|
+
No secrets stored in code - uses standard AWS authentication.
|
|
143
|
+
"""
|
|
144
|
+
try:
|
|
145
|
+
import boto3
|
|
146
|
+
except ImportError:
|
|
147
|
+
raise ImportError(
|
|
148
|
+
"boto3 is required for Bedrock embeddings. Install with: pip install boto3"
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Create Bedrock client using AWS profile/credentials
|
|
152
|
+
session = boto3.Session(region_name=AWS_REGION)
|
|
153
|
+
bedrock = session.client("bedrock-runtime")
|
|
154
|
+
|
|
155
|
+
# Prepare request based on model
|
|
156
|
+
if "titan-embed" in EMBEDDING_MODEL:
|
|
157
|
+
# Amazon Titan Embed format
|
|
158
|
+
body = json.dumps({
|
|
159
|
+
"inputText": text
|
|
160
|
+
})
|
|
161
|
+
content_type = "application/json"
|
|
162
|
+
accept = "application/json"
|
|
163
|
+
elif "cohere" in EMBEDDING_MODEL:
|
|
164
|
+
# Cohere Embed format
|
|
165
|
+
body = json.dumps({
|
|
166
|
+
"texts": [text],
|
|
167
|
+
"input_type": "search_document"
|
|
168
|
+
})
|
|
169
|
+
content_type = "application/json"
|
|
170
|
+
accept = "application/json"
|
|
171
|
+
else:
|
|
172
|
+
raise ValueError(f"Unsupported Bedrock model: {EMBEDDING_MODEL}")
|
|
173
|
+
|
|
174
|
+
# Invoke the model
|
|
175
|
+
response = bedrock.invoke_model(
|
|
176
|
+
modelId=EMBEDDING_MODEL,
|
|
177
|
+
body=body,
|
|
178
|
+
contentType=content_type,
|
|
179
|
+
accept=accept
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
# Parse response
|
|
183
|
+
result = json.loads(response["body"].read())
|
|
184
|
+
|
|
185
|
+
if "titan-embed" in EMBEDDING_MODEL:
|
|
186
|
+
return result["embedding"]
|
|
187
|
+
elif "cohere" in EMBEDDING_MODEL:
|
|
188
|
+
return result["embeddings"][0]
|
|
189
|
+
|
|
190
|
+
return result.get("embedding", result.get("embeddings", [[]])[0])
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def get_embedding(text: str) -> List[float]:
|
|
194
|
+
"""
|
|
195
|
+
Generate embedding using the configured provider.
|
|
196
|
+
|
|
197
|
+
Default: Ollama (local/private)
|
|
198
|
+
Override with EMBEDDING_PROVIDER environment variable.
|
|
199
|
+
|
|
200
|
+
Providers:
|
|
201
|
+
- ollama: Local, free, private (default)
|
|
202
|
+
- bedrock: AWS Titan Embed V2 (~$0.02/1M tokens)
|
|
203
|
+
- openai: OpenAI API (~$0.02/1M tokens)
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
text: Text to embed
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
List of floats representing the embedding vector
|
|
210
|
+
|
|
211
|
+
Raises:
|
|
212
|
+
ValueError: If provider is not configured correctly
|
|
213
|
+
URLError: If unable to connect to embedding service
|
|
214
|
+
"""
|
|
215
|
+
if EMBEDDING_PROVIDER == "ollama":
|
|
216
|
+
return get_embedding_ollama(text)
|
|
217
|
+
elif EMBEDDING_PROVIDER == "openai":
|
|
218
|
+
return get_embedding_openai(text)
|
|
219
|
+
elif EMBEDDING_PROVIDER == "bedrock":
|
|
220
|
+
return get_embedding_bedrock(text)
|
|
221
|
+
else:
|
|
222
|
+
raise ValueError(
|
|
223
|
+
f"Unknown embedding provider: {EMBEDDING_PROVIDER}. "
|
|
224
|
+
f"Use 'ollama', 'openai', or 'bedrock'."
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def check_embedding_service() -> dict:
|
|
229
|
+
"""
|
|
230
|
+
Check if the embedding service is available.
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
Dict with status and details
|
|
234
|
+
"""
|
|
235
|
+
try:
|
|
236
|
+
if EMBEDDING_PROVIDER == "ollama":
|
|
237
|
+
req = Request(f"{OLLAMA_URL}/api/tags", method="GET")
|
|
238
|
+
with urlopen(req, timeout=10) as response:
|
|
239
|
+
result = json.loads(response.read().decode())
|
|
240
|
+
models = [m["name"] for m in result.get("models", [])]
|
|
241
|
+
has_model = any(EMBEDDING_MODEL in m for m in models)
|
|
242
|
+
return {
|
|
243
|
+
"status": "ok" if has_model else "missing_model",
|
|
244
|
+
"provider": "ollama",
|
|
245
|
+
"url": OLLAMA_URL,
|
|
246
|
+
"model": EMBEDDING_MODEL,
|
|
247
|
+
"available_models": models,
|
|
248
|
+
"message": f"Model '{EMBEDDING_MODEL}' ready" if has_model else f"Run: ollama pull {EMBEDDING_MODEL}"
|
|
249
|
+
}
|
|
250
|
+
elif EMBEDDING_PROVIDER == "openai":
|
|
251
|
+
if not OPENAI_API_KEY:
|
|
252
|
+
return {
|
|
253
|
+
"status": "missing_key",
|
|
254
|
+
"provider": "openai",
|
|
255
|
+
"model": EMBEDDING_MODEL,
|
|
256
|
+
"message": "Set OPENAI_API_KEY environment variable"
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
"status": "ok",
|
|
260
|
+
"provider": "openai",
|
|
261
|
+
"model": EMBEDDING_MODEL,
|
|
262
|
+
"message": "OpenAI API key configured"
|
|
263
|
+
}
|
|
264
|
+
elif EMBEDDING_PROVIDER == "bedrock":
|
|
265
|
+
try:
|
|
266
|
+
import boto3
|
|
267
|
+
session = boto3.Session(region_name=AWS_REGION)
|
|
268
|
+
sts = session.client("sts")
|
|
269
|
+
identity = sts.get_caller_identity()
|
|
270
|
+
return {
|
|
271
|
+
"status": "ok",
|
|
272
|
+
"provider": "bedrock",
|
|
273
|
+
"model": EMBEDDING_MODEL,
|
|
274
|
+
"region": AWS_REGION,
|
|
275
|
+
"account": identity.get("Account", "unknown"),
|
|
276
|
+
"message": f"AWS authenticated (account: {identity.get('Account', 'unknown')})"
|
|
277
|
+
}
|
|
278
|
+
except ImportError:
|
|
279
|
+
return {
|
|
280
|
+
"status": "missing_boto3",
|
|
281
|
+
"provider": "bedrock",
|
|
282
|
+
"message": "Install boto3: pip install boto3"
|
|
283
|
+
}
|
|
284
|
+
except Exception as e:
|
|
285
|
+
return {
|
|
286
|
+
"status": "auth_error",
|
|
287
|
+
"provider": "bedrock",
|
|
288
|
+
"message": f"AWS auth failed: {e}"
|
|
289
|
+
}
|
|
290
|
+
else:
|
|
291
|
+
return {
|
|
292
|
+
"status": "unknown_provider",
|
|
293
|
+
"provider": EMBEDDING_PROVIDER,
|
|
294
|
+
"message": f"Unknown provider. Use 'ollama', 'openai', or 'bedrock'."
|
|
295
|
+
}
|
|
296
|
+
except URLError as e:
|
|
297
|
+
return {
|
|
298
|
+
"status": "connection_error",
|
|
299
|
+
"provider": EMBEDDING_PROVIDER,
|
|
300
|
+
"message": str(e)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
if __name__ == "__main__":
|
|
305
|
+
# Quick test
|
|
306
|
+
import sys
|
|
307
|
+
|
|
308
|
+
print(f"Embedding Provider: {EMBEDDING_PROVIDER}")
|
|
309
|
+
print(f"Model: {EMBEDDING_MODEL}")
|
|
310
|
+
print(f"Dimensions: {get_embedding_dimension()}")
|
|
311
|
+
print()
|
|
312
|
+
|
|
313
|
+
status = check_embedding_service()
|
|
314
|
+
print(f"Service Status: {status['status']}")
|
|
315
|
+
print(f"Message: {status['message']}")
|
|
316
|
+
|
|
317
|
+
if status["status"] == "ok":
|
|
318
|
+
try:
|
|
319
|
+
embedding = get_embedding("Test embedding generation")
|
|
320
|
+
print(f"\n✓ Embedding generated: {len(embedding)} dimensions")
|
|
321
|
+
except Exception as e:
|
|
322
|
+
print(f"\n✗ Error: {e}")
|
|
323
|
+
sys.exit(1)
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Script: hybrid_search.py
|
|
4
|
+
Purpose: Hybrid search combining vector similarity with keyword filtering.
|
|
5
|
+
|
|
6
|
+
Supports both Ollama (local/private) and OpenAI (cloud) embeddings.
|
|
7
|
+
Default: Ollama with nomic-embed-text model.
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
python3 hybrid_search.py --query "kubernetes deployment failed" \\
|
|
11
|
+
--keyword error_code=ImagePullBackOff --keyword namespace=production
|
|
12
|
+
|
|
13
|
+
This is particularly useful for technical queries where you need:
|
|
14
|
+
- Semantic understanding (what is the user asking about?)
|
|
15
|
+
- Exact matching (specific error codes, variable names, identifiers)
|
|
16
|
+
|
|
17
|
+
Environment Variables:
|
|
18
|
+
EMBEDDING_PROVIDER - "ollama" (default) or "openai"
|
|
19
|
+
OLLAMA_URL - Ollama server URL (default: http://localhost:11434)
|
|
20
|
+
OPENAI_API_KEY - Required for OpenAI provider
|
|
21
|
+
QDRANT_URL - Qdrant server URL (default: http://localhost:6333)
|
|
22
|
+
MEMORY_COLLECTION - Collection name (default: agent_memory)
|
|
23
|
+
|
|
24
|
+
Exit Codes:
|
|
25
|
+
0 - Success (results found)
|
|
26
|
+
1 - No results
|
|
27
|
+
2 - Connection error
|
|
28
|
+
3 - Search error
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
import argparse
|
|
32
|
+
import json
|
|
33
|
+
import os
|
|
34
|
+
import sys
|
|
35
|
+
from typing import List, Dict, Any, Optional
|
|
36
|
+
from urllib.request import Request, urlopen
|
|
37
|
+
from urllib.error import URLError
|
|
38
|
+
|
|
39
|
+
# Import shared embedding utilities (supports Ollama and OpenAI)
|
|
40
|
+
try:
|
|
41
|
+
from embedding_utils import get_embedding
|
|
42
|
+
except ImportError:
|
|
43
|
+
# Fallback if run from different directory
|
|
44
|
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
45
|
+
from embedding_utils import get_embedding
|
|
46
|
+
|
|
47
|
+
# Configuration
|
|
48
|
+
QDRANT_URL = os.environ.get("QDRANT_URL", "http://localhost:6333")
|
|
49
|
+
COLLECTION = os.environ.get("MEMORY_COLLECTION", "agent_memory")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def hybrid_query(
|
|
53
|
+
text_query: str,
|
|
54
|
+
keyword_filters: Optional[Dict[str, str]] = None,
|
|
55
|
+
must_not_filters: Optional[Dict[str, str]] = None,
|
|
56
|
+
top_k: int = 10,
|
|
57
|
+
score_threshold: float = 0.6
|
|
58
|
+
) -> Dict[str, Any]:
|
|
59
|
+
"""
|
|
60
|
+
Perform hybrid search combining vector similarity with keyword filtering.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
text_query: Natural language query for semantic search
|
|
64
|
+
keyword_filters: Dict of field:value pairs that MUST match
|
|
65
|
+
must_not_filters: Dict of field:value pairs that MUST NOT match
|
|
66
|
+
top_k: Number of results
|
|
67
|
+
score_threshold: Minimum similarity score
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
Search results with hybrid scores
|
|
71
|
+
"""
|
|
72
|
+
# Get embedding for semantic search
|
|
73
|
+
embedding = get_embedding(text_query)
|
|
74
|
+
|
|
75
|
+
# Build filter conditions
|
|
76
|
+
filter_conditions = {"must": [], "must_not": []}
|
|
77
|
+
|
|
78
|
+
if keyword_filters:
|
|
79
|
+
for field, value in keyword_filters.items():
|
|
80
|
+
filter_conditions["must"].append({
|
|
81
|
+
"key": field,
|
|
82
|
+
"match": {"value": value}
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
if must_not_filters:
|
|
86
|
+
for field, value in must_not_filters.items():
|
|
87
|
+
filter_conditions["must_not"].append({
|
|
88
|
+
"key": field,
|
|
89
|
+
"match": {"value": value}
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
# Build search payload
|
|
93
|
+
search_payload = {
|
|
94
|
+
"vector": embedding,
|
|
95
|
+
"limit": top_k,
|
|
96
|
+
"score_threshold": score_threshold,
|
|
97
|
+
"with_payload": True
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
# Only add filter if we have conditions
|
|
101
|
+
if filter_conditions["must"] or filter_conditions["must_not"]:
|
|
102
|
+
search_payload["filter"] = {
|
|
103
|
+
k: v for k, v in filter_conditions.items() if v
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
req = Request(
|
|
107
|
+
f"{QDRANT_URL}/collections/{COLLECTION}/points/search",
|
|
108
|
+
data=json.dumps(search_payload).encode(),
|
|
109
|
+
headers={"Content-Type": "application/json"},
|
|
110
|
+
method="POST"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
with urlopen(req, timeout=30) as response:
|
|
114
|
+
result = json.loads(response.read().decode())
|
|
115
|
+
|
|
116
|
+
results = []
|
|
117
|
+
for hit in result.get("result", []):
|
|
118
|
+
results.append({
|
|
119
|
+
"id": hit["id"],
|
|
120
|
+
"score": hit["score"],
|
|
121
|
+
"content": hit["payload"].get("content", ""),
|
|
122
|
+
"type": hit["payload"].get("type"),
|
|
123
|
+
"project": hit["payload"].get("project"),
|
|
124
|
+
"tags": hit["payload"].get("tags", []),
|
|
125
|
+
"timestamp": hit["payload"].get("timestamp"),
|
|
126
|
+
"matched_filters": keyword_filters or {}
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
"query": text_query,
|
|
131
|
+
"keyword_filters": keyword_filters,
|
|
132
|
+
"results": results,
|
|
133
|
+
"total": len(results),
|
|
134
|
+
"search_type": "hybrid" if keyword_filters else "semantic"
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def parse_keyword_args(keyword_args: List[str]) -> Dict[str, str]:
|
|
139
|
+
"""Parse keyword arguments in format key=value."""
|
|
140
|
+
filters = {}
|
|
141
|
+
for kv in keyword_args or []:
|
|
142
|
+
if "=" in kv:
|
|
143
|
+
key, value = kv.split("=", 1)
|
|
144
|
+
filters[key.strip()] = value.strip()
|
|
145
|
+
return filters
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def main():
|
|
149
|
+
parser = argparse.ArgumentParser(
|
|
150
|
+
description="Hybrid search with vector + keyword filtering"
|
|
151
|
+
)
|
|
152
|
+
parser.add_argument(
|
|
153
|
+
"--query",
|
|
154
|
+
required=True,
|
|
155
|
+
help="Natural language query"
|
|
156
|
+
)
|
|
157
|
+
parser.add_argument(
|
|
158
|
+
"--keyword",
|
|
159
|
+
action="append",
|
|
160
|
+
help="Keyword filter in key=value format (can be repeated)"
|
|
161
|
+
)
|
|
162
|
+
parser.add_argument(
|
|
163
|
+
"--exclude",
|
|
164
|
+
action="append",
|
|
165
|
+
help="Exclusion filter in key=value format (can be repeated)"
|
|
166
|
+
)
|
|
167
|
+
parser.add_argument(
|
|
168
|
+
"--top-k",
|
|
169
|
+
type=int,
|
|
170
|
+
default=10,
|
|
171
|
+
help="Number of results (default: 10)"
|
|
172
|
+
)
|
|
173
|
+
parser.add_argument(
|
|
174
|
+
"--threshold",
|
|
175
|
+
type=float,
|
|
176
|
+
default=0.6,
|
|
177
|
+
help="Minimum score threshold (default: 0.6)"
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
args = parser.parse_args()
|
|
181
|
+
|
|
182
|
+
try:
|
|
183
|
+
keyword_filters = parse_keyword_args(args.keyword)
|
|
184
|
+
exclude_filters = parse_keyword_args(args.exclude)
|
|
185
|
+
|
|
186
|
+
result = hybrid_query(
|
|
187
|
+
text_query=args.query,
|
|
188
|
+
keyword_filters=keyword_filters if keyword_filters else None,
|
|
189
|
+
must_not_filters=exclude_filters if exclude_filters else None,
|
|
190
|
+
top_k=args.top_k,
|
|
191
|
+
score_threshold=args.threshold
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
print(json.dumps(result, indent=2))
|
|
195
|
+
sys.exit(0 if result["total"] > 0 else 1)
|
|
196
|
+
|
|
197
|
+
except URLError as e:
|
|
198
|
+
print(json.dumps({
|
|
199
|
+
"status": "error",
|
|
200
|
+
"type": "connection_error",
|
|
201
|
+
"message": str(e)
|
|
202
|
+
}), file=sys.stderr)
|
|
203
|
+
sys.exit(2)
|
|
204
|
+
except Exception as e:
|
|
205
|
+
print(json.dumps({
|
|
206
|
+
"status": "error",
|
|
207
|
+
"type": type(e).__name__,
|
|
208
|
+
"message": str(e)
|
|
209
|
+
}), file=sys.stderr)
|
|
210
|
+
sys.exit(3)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
if __name__ == "__main__":
|
|
214
|
+
main()
|