@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.
Files changed (196) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +147 -0
  3. package/bin/init.js +471 -0
  4. package/package.json +36 -0
  5. package/templates/.agent/agents/backend-specialist.md +263 -0
  6. package/templates/.agent/agents/code-archaeologist.md +106 -0
  7. package/templates/.agent/agents/database-architect.md +226 -0
  8. package/templates/.agent/agents/debugger.md +225 -0
  9. package/templates/.agent/agents/devops-engineer.md +242 -0
  10. package/templates/.agent/agents/documentation-writer.md +104 -0
  11. package/templates/.agent/agents/explorer-agent.md +73 -0
  12. package/templates/.agent/agents/frontend-specialist.md +556 -0
  13. package/templates/.agent/agents/game-developer.md +162 -0
  14. package/templates/.agent/agents/mobile-developer.md +377 -0
  15. package/templates/.agent/agents/orchestrator.md +416 -0
  16. package/templates/.agent/agents/penetration-tester.md +188 -0
  17. package/templates/.agent/agents/performance-optimizer.md +187 -0
  18. package/templates/.agent/agents/product-manager.md +112 -0
  19. package/templates/.agent/agents/project-planner.md +403 -0
  20. package/templates/.agent/agents/qa-automation-engineer.md +109 -0
  21. package/templates/.agent/agents/security-auditor.md +170 -0
  22. package/templates/.agent/agents/seo-specialist.md +111 -0
  23. package/templates/.agent/agents/test-engineer.md +158 -0
  24. package/templates/.agent/rules/GEMINI.md +253 -0
  25. package/templates/.agent/workflows/brainstorm.md +113 -0
  26. package/templates/.agent/workflows/create.md +59 -0
  27. package/templates/.agent/workflows/debug.md +103 -0
  28. package/templates/.agent/workflows/deploy.md +176 -0
  29. package/templates/.agent/workflows/enhance.md +63 -0
  30. package/templates/.agent/workflows/orchestrate.md +237 -0
  31. package/templates/.agent/workflows/plan.md +89 -0
  32. package/templates/.agent/workflows/preview.md +81 -0
  33. package/templates/.agent/workflows/status.md +86 -0
  34. package/templates/.agent/workflows/test.md +144 -0
  35. package/templates/.agent/workflows/ui-ux-pro-max.md +296 -0
  36. package/templates/base/.env.example +54 -0
  37. package/templates/base/AGENTS.md +463 -0
  38. package/templates/base/requirements.txt +6 -0
  39. package/templates/base/skill-creator/LICENSE.txt +202 -0
  40. package/templates/base/skill-creator/SKILL_skillcreator.md +389 -0
  41. package/templates/base/skill-creator/references/output-patterns.md +82 -0
  42. package/templates/base/skill-creator/references/workflows.md +28 -0
  43. package/templates/base/skill-creator/scripts/init_skill.py +304 -0
  44. package/templates/base/skill-creator/scripts/package_skill.py +110 -0
  45. package/templates/base/skill-creator/scripts/quick_validate.py +95 -0
  46. package/templates/base/skill-creator/scripts/update_catalog.py +371 -0
  47. package/templates/skills/core/README.md +21 -0
  48. package/templates/skills/core/documentation/SKILL.md +351 -0
  49. package/templates/skills/core/documentation/references/best_practices.md +201 -0
  50. package/templates/skills/core/documentation/scripts/analyze_code.py +307 -0
  51. package/templates/skills/core/documentation/scripts/detect_changes.py +460 -0
  52. package/templates/skills/core/documentation/scripts/generate_changelog.py +312 -0
  53. package/templates/skills/core/documentation/scripts/sync_docs.py +272 -0
  54. package/templates/skills/core/documentation/scripts/update_skill_docs.py +366 -0
  55. package/templates/skills/core/pdf-reader/SKILL.md +104 -0
  56. package/templates/skills/core/pdf-reader/references/pdf_libraries.md +83 -0
  57. package/templates/skills/core/pdf-reader/scripts/extract_text.py +295 -0
  58. package/templates/skills/core/qdrant-memory/SKILL.md +435 -0
  59. package/templates/skills/core/qdrant-memory/references/advanced_patterns.md +375 -0
  60. package/templates/skills/core/qdrant-memory/references/collection_schemas.md +229 -0
  61. package/templates/skills/core/qdrant-memory/references/complete_guide.md +724 -0
  62. package/templates/skills/core/qdrant-memory/references/embedding_models.md +325 -0
  63. package/templates/skills/core/qdrant-memory/scripts/benchmark_token_savings.py +640 -0
  64. package/templates/skills/core/qdrant-memory/scripts/embedding_utils.py +323 -0
  65. package/templates/skills/core/qdrant-memory/scripts/hybrid_search.py +214 -0
  66. package/templates/skills/core/qdrant-memory/scripts/init_collection.py +193 -0
  67. package/templates/skills/core/qdrant-memory/scripts/memory_retrieval.py +345 -0
  68. package/templates/skills/core/qdrant-memory/scripts/semantic_cache.py +282 -0
  69. package/templates/skills/core/qdrant-memory/scripts/test_skill.py +655 -0
  70. package/templates/skills/core/webcrawler/SKILL.md +292 -0
  71. package/templates/skills/core/webcrawler/references/advanced_crawling.md +181 -0
  72. package/templates/skills/core/webcrawler/scripts/crawl_docs.py +532 -0
  73. package/templates/skills/core/webcrawler/scripts/extract_page.py +189 -0
  74. package/templates/skills/core/webcrawler/scripts/filter_docs.py +200 -0
  75. package/templates/skills/knowledge/api-patterns/SKILL.md +81 -0
  76. package/templates/skills/knowledge/api-patterns/api-style.md +42 -0
  77. package/templates/skills/knowledge/api-patterns/auth.md +24 -0
  78. package/templates/skills/knowledge/api-patterns/documentation.md +26 -0
  79. package/templates/skills/knowledge/api-patterns/graphql.md +41 -0
  80. package/templates/skills/knowledge/api-patterns/rate-limiting.md +31 -0
  81. package/templates/skills/knowledge/api-patterns/response.md +37 -0
  82. package/templates/skills/knowledge/api-patterns/rest.md +40 -0
  83. package/templates/skills/knowledge/api-patterns/scripts/api_validator.py +211 -0
  84. package/templates/skills/knowledge/api-patterns/security-testing.md +122 -0
  85. package/templates/skills/knowledge/api-patterns/trpc.md +41 -0
  86. package/templates/skills/knowledge/api-patterns/versioning.md +22 -0
  87. package/templates/skills/knowledge/app-builder/SKILL.md +75 -0
  88. package/templates/skills/knowledge/app-builder/agent-coordination.md +71 -0
  89. package/templates/skills/knowledge/app-builder/feature-building.md +53 -0
  90. package/templates/skills/knowledge/app-builder/project-detection.md +34 -0
  91. package/templates/skills/knowledge/app-builder/scaffolding.md +118 -0
  92. package/templates/skills/knowledge/app-builder/tech-stack.md +40 -0
  93. package/templates/skills/knowledge/app-builder/templates/SKILL.md +39 -0
  94. package/templates/skills/knowledge/app-builder/templates/astro-static/TEMPLATE.md +76 -0
  95. package/templates/skills/knowledge/app-builder/templates/chrome-extension/TEMPLATE.md +92 -0
  96. package/templates/skills/knowledge/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
  97. package/templates/skills/knowledge/app-builder/templates/electron-desktop/TEMPLATE.md +88 -0
  98. package/templates/skills/knowledge/app-builder/templates/express-api/TEMPLATE.md +83 -0
  99. package/templates/skills/knowledge/app-builder/templates/flutter-app/TEMPLATE.md +90 -0
  100. package/templates/skills/knowledge/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -0
  101. package/templates/skills/knowledge/app-builder/templates/nextjs-fullstack/TEMPLATE.md +82 -0
  102. package/templates/skills/knowledge/app-builder/templates/nextjs-saas/TEMPLATE.md +100 -0
  103. package/templates/skills/knowledge/app-builder/templates/nextjs-static/TEMPLATE.md +106 -0
  104. package/templates/skills/knowledge/app-builder/templates/nuxt-app/TEMPLATE.md +101 -0
  105. package/templates/skills/knowledge/app-builder/templates/python-fastapi/TEMPLATE.md +83 -0
  106. package/templates/skills/knowledge/app-builder/templates/react-native-app/TEMPLATE.md +93 -0
  107. package/templates/skills/knowledge/architecture/SKILL.md +55 -0
  108. package/templates/skills/knowledge/architecture/context-discovery.md +43 -0
  109. package/templates/skills/knowledge/architecture/examples.md +94 -0
  110. package/templates/skills/knowledge/architecture/pattern-selection.md +68 -0
  111. package/templates/skills/knowledge/architecture/patterns-reference.md +50 -0
  112. package/templates/skills/knowledge/architecture/trade-off-analysis.md +77 -0
  113. package/templates/skills/knowledge/bash-linux/SKILL.md +199 -0
  114. package/templates/skills/knowledge/behavioral-modes/SKILL.md +242 -0
  115. package/templates/skills/knowledge/brainstorming/SKILL.md +163 -0
  116. package/templates/skills/knowledge/brainstorming/dynamic-questioning.md +350 -0
  117. package/templates/skills/knowledge/clean-code/SKILL.md +201 -0
  118. package/templates/skills/knowledge/code-review-checklist/SKILL.md +109 -0
  119. package/templates/skills/knowledge/database-design/SKILL.md +52 -0
  120. package/templates/skills/knowledge/database-design/database-selection.md +43 -0
  121. package/templates/skills/knowledge/database-design/indexing.md +39 -0
  122. package/templates/skills/knowledge/database-design/migrations.md +48 -0
  123. package/templates/skills/knowledge/database-design/optimization.md +36 -0
  124. package/templates/skills/knowledge/database-design/orm-selection.md +30 -0
  125. package/templates/skills/knowledge/database-design/schema-design.md +56 -0
  126. package/templates/skills/knowledge/database-design/scripts/schema_validator.py +172 -0
  127. package/templates/skills/knowledge/deployment-procedures/SKILL.md +241 -0
  128. package/templates/skills/knowledge/doc.md +177 -0
  129. package/templates/skills/knowledge/documentation-templates/SKILL.md +194 -0
  130. package/templates/skills/knowledge/frontend-design/SKILL.md +396 -0
  131. package/templates/skills/knowledge/frontend-design/animation-guide.md +331 -0
  132. package/templates/skills/knowledge/frontend-design/color-system.md +311 -0
  133. package/templates/skills/knowledge/frontend-design/decision-trees.md +418 -0
  134. package/templates/skills/knowledge/frontend-design/motion-graphics.md +306 -0
  135. package/templates/skills/knowledge/frontend-design/scripts/accessibility_checker.py +183 -0
  136. package/templates/skills/knowledge/frontend-design/scripts/ux_audit.py +722 -0
  137. package/templates/skills/knowledge/frontend-design/typography-system.md +345 -0
  138. package/templates/skills/knowledge/frontend-design/ux-psychology.md +541 -0
  139. package/templates/skills/knowledge/frontend-design/visual-effects.md +383 -0
  140. package/templates/skills/knowledge/game-development/2d-games/SKILL.md +119 -0
  141. package/templates/skills/knowledge/game-development/3d-games/SKILL.md +135 -0
  142. package/templates/skills/knowledge/game-development/SKILL.md +167 -0
  143. package/templates/skills/knowledge/game-development/game-art/SKILL.md +185 -0
  144. package/templates/skills/knowledge/game-development/game-audio/SKILL.md +190 -0
  145. package/templates/skills/knowledge/game-development/game-design/SKILL.md +129 -0
  146. package/templates/skills/knowledge/game-development/mobile-games/SKILL.md +108 -0
  147. package/templates/skills/knowledge/game-development/multiplayer/SKILL.md +132 -0
  148. package/templates/skills/knowledge/game-development/pc-games/SKILL.md +144 -0
  149. package/templates/skills/knowledge/game-development/vr-ar/SKILL.md +123 -0
  150. package/templates/skills/knowledge/game-development/web-games/SKILL.md +150 -0
  151. package/templates/skills/knowledge/geo-fundamentals/SKILL.md +156 -0
  152. package/templates/skills/knowledge/geo-fundamentals/scripts/geo_checker.py +289 -0
  153. package/templates/skills/knowledge/i18n-localization/SKILL.md +154 -0
  154. package/templates/skills/knowledge/i18n-localization/scripts/i18n_checker.py +241 -0
  155. package/templates/skills/knowledge/intelligent-routing/SKILL.md +334 -0
  156. package/templates/skills/knowledge/lint-and-validate/SKILL.md +45 -0
  157. package/templates/skills/knowledge/lint-and-validate/scripts/lint_runner.py +172 -0
  158. package/templates/skills/knowledge/lint-and-validate/scripts/type_coverage.py +173 -0
  159. package/templates/skills/knowledge/mcp-builder/SKILL.md +176 -0
  160. package/templates/skills/knowledge/mobile-design/SKILL.md +394 -0
  161. package/templates/skills/knowledge/mobile-design/decision-trees.md +516 -0
  162. package/templates/skills/knowledge/mobile-design/mobile-backend.md +491 -0
  163. package/templates/skills/knowledge/mobile-design/mobile-color-system.md +420 -0
  164. package/templates/skills/knowledge/mobile-design/mobile-debugging.md +122 -0
  165. package/templates/skills/knowledge/mobile-design/mobile-design-thinking.md +357 -0
  166. package/templates/skills/knowledge/mobile-design/mobile-navigation.md +458 -0
  167. package/templates/skills/knowledge/mobile-design/mobile-performance.md +767 -0
  168. package/templates/skills/knowledge/mobile-design/mobile-testing.md +356 -0
  169. package/templates/skills/knowledge/mobile-design/mobile-typography.md +433 -0
  170. package/templates/skills/knowledge/mobile-design/platform-android.md +666 -0
  171. package/templates/skills/knowledge/mobile-design/platform-ios.md +561 -0
  172. package/templates/skills/knowledge/mobile-design/scripts/mobile_audit.py +670 -0
  173. package/templates/skills/knowledge/mobile-design/touch-psychology.md +537 -0
  174. package/templates/skills/knowledge/nextjs-best-practices/SKILL.md +203 -0
  175. package/templates/skills/knowledge/nodejs-best-practices/SKILL.md +333 -0
  176. package/templates/skills/knowledge/parallel-agents/SKILL.md +175 -0
  177. package/templates/skills/knowledge/performance-profiling/SKILL.md +143 -0
  178. package/templates/skills/knowledge/performance-profiling/scripts/lighthouse_audit.py +76 -0
  179. package/templates/skills/knowledge/plan-writing/SKILL.md +152 -0
  180. package/templates/skills/knowledge/powershell-windows/SKILL.md +167 -0
  181. package/templates/skills/knowledge/python-patterns/SKILL.md +441 -0
  182. package/templates/skills/knowledge/react-patterns/SKILL.md +198 -0
  183. package/templates/skills/knowledge/red-team-tactics/SKILL.md +199 -0
  184. package/templates/skills/knowledge/seo-fundamentals/SKILL.md +129 -0
  185. package/templates/skills/knowledge/seo-fundamentals/scripts/seo_checker.py +219 -0
  186. package/templates/skills/knowledge/server-management/SKILL.md +161 -0
  187. package/templates/skills/knowledge/systematic-debugging/SKILL.md +109 -0
  188. package/templates/skills/knowledge/tailwind-patterns/SKILL.md +269 -0
  189. package/templates/skills/knowledge/tdd-workflow/SKILL.md +149 -0
  190. package/templates/skills/knowledge/testing-patterns/SKILL.md +178 -0
  191. package/templates/skills/knowledge/testing-patterns/scripts/test_runner.py +219 -0
  192. package/templates/skills/knowledge/vulnerability-scanner/SKILL.md +276 -0
  193. package/templates/skills/knowledge/vulnerability-scanner/checklists.md +121 -0
  194. package/templates/skills/knowledge/vulnerability-scanner/scripts/security_scan.py +458 -0
  195. package/templates/skills/knowledge/webapp-testing/SKILL.md +187 -0
  196. 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()