maestro-bundle 1.3.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/templates/bundle-ai-agents/skills/agent-orchestration/SKILL.md +107 -41
- package/templates/bundle-ai-agents/skills/agent-orchestration/references/graph-patterns.md +50 -0
- package/templates/bundle-ai-agents/skills/agent-orchestration/references/routing-strategies.md +47 -0
- package/templates/bundle-ai-agents/skills/api-design/SKILL.md +125 -16
- package/templates/bundle-ai-agents/skills/api-design/references/pydantic-patterns.md +72 -0
- package/templates/bundle-ai-agents/skills/api-design/references/rest-conventions.md +51 -0
- package/templates/bundle-ai-agents/skills/clean-architecture/SKILL.md +113 -21
- package/templates/bundle-ai-agents/skills/clean-architecture/references/dependency-injection.md +60 -0
- package/templates/bundle-ai-agents/skills/clean-architecture/references/layer-rules.md +56 -0
- package/templates/bundle-ai-agents/skills/context-engineering/SKILL.md +104 -36
- package/templates/bundle-ai-agents/skills/context-engineering/references/compression-techniques.md +76 -0
- package/templates/bundle-ai-agents/skills/context-engineering/references/context-budget-calculator.md +45 -0
- package/templates/bundle-ai-agents/skills/database-modeling/SKILL.md +146 -19
- package/templates/bundle-ai-agents/skills/database-modeling/references/index-strategies.md +48 -0
- package/templates/bundle-ai-agents/skills/database-modeling/references/naming-conventions.md +27 -0
- package/templates/bundle-ai-agents/skills/docker-containerization/SKILL.md +124 -15
- package/templates/bundle-ai-agents/skills/docker-containerization/references/compose-patterns.md +97 -0
- package/templates/bundle-ai-agents/skills/docker-containerization/references/dockerfile-checklist.md +37 -0
- package/templates/bundle-ai-agents/skills/eval-testing/SKILL.md +113 -25
- package/templates/bundle-ai-agents/skills/eval-testing/references/eval-types.md +52 -0
- package/templates/bundle-ai-agents/skills/eval-testing/references/golden-dataset-template.md +59 -0
- package/templates/bundle-ai-agents/skills/memory-management/SKILL.md +112 -28
- package/templates/bundle-ai-agents/skills/memory-management/references/memory-tiers.md +41 -0
- package/templates/bundle-ai-agents/skills/memory-management/references/namespace-conventions.md +41 -0
- package/templates/bundle-ai-agents/skills/prompt-engineering/SKILL.md +139 -47
- package/templates/bundle-ai-agents/skills/prompt-engineering/references/anti-patterns.md +59 -0
- package/templates/bundle-ai-agents/skills/prompt-engineering/references/prompt-templates.md +75 -0
- package/templates/bundle-ai-agents/skills/rag-pipeline/SKILL.md +104 -27
- package/templates/bundle-ai-agents/skills/rag-pipeline/references/chunking-strategies.md +27 -0
- package/templates/bundle-ai-agents/skills/rag-pipeline/references/embedding-models.md +31 -0
- package/templates/bundle-ai-agents/skills/rag-pipeline/references/rag-evaluation.md +39 -0
- package/templates/bundle-ai-agents/skills/testing-strategy/SKILL.md +127 -18
- package/templates/bundle-ai-agents/skills/testing-strategy/references/fixture-patterns.md +81 -0
- package/templates/bundle-ai-agents/skills/testing-strategy/references/naming-conventions.md +69 -0
- package/templates/bundle-base/skills/branch-strategy/SKILL.md +134 -21
- package/templates/bundle-base/skills/branch-strategy/references/branch-rules.md +40 -0
- package/templates/bundle-base/skills/code-review/SKILL.md +123 -38
- package/templates/bundle-base/skills/code-review/references/review-checklist.md +45 -0
- package/templates/bundle-base/skills/commit-pattern/SKILL.md +98 -39
- package/templates/bundle-base/skills/commit-pattern/references/conventional-commits.md +40 -0
- package/templates/bundle-data-pipeline/skills/data-preprocessing/SKILL.md +110 -19
- package/templates/bundle-data-pipeline/skills/data-preprocessing/references/pandas-cheatsheet.md +63 -0
- package/templates/bundle-data-pipeline/skills/data-preprocessing/references/pandera-schemas.md +44 -0
- package/templates/bundle-data-pipeline/skills/docker-containerization/SKILL.md +132 -16
- package/templates/bundle-data-pipeline/skills/docker-containerization/references/compose-patterns.md +82 -0
- package/templates/bundle-data-pipeline/skills/docker-containerization/references/dockerfile-best-practices.md +57 -0
- package/templates/bundle-data-pipeline/skills/feature-engineering/SKILL.md +143 -45
- package/templates/bundle-data-pipeline/skills/feature-engineering/references/encoding-guide.md +41 -0
- package/templates/bundle-data-pipeline/skills/feature-engineering/references/scaling-guide.md +38 -0
- package/templates/bundle-data-pipeline/skills/mlops-pipeline/SKILL.md +156 -37
- package/templates/bundle-data-pipeline/skills/mlops-pipeline/references/mlflow-commands.md +69 -0
- package/templates/bundle-data-pipeline/skills/model-training/SKILL.md +152 -33
- package/templates/bundle-data-pipeline/skills/model-training/references/evaluation-metrics.md +52 -0
- package/templates/bundle-data-pipeline/skills/model-training/references/model-selection-guide.md +41 -0
- package/templates/bundle-data-pipeline/skills/rag-pipeline/SKILL.md +127 -39
- package/templates/bundle-data-pipeline/skills/rag-pipeline/references/chunking-strategies.md +51 -0
- package/templates/bundle-data-pipeline/skills/rag-pipeline/references/embedding-models.md +49 -0
- package/templates/bundle-frontend-spa/skills/authentication/SKILL.md +196 -13
- package/templates/bundle-frontend-spa/skills/authentication/references/jwt-security.md +41 -0
- package/templates/bundle-frontend-spa/skills/component-design/SKILL.md +191 -41
- package/templates/bundle-frontend-spa/skills/component-design/references/accessibility-checklist.md +41 -0
- package/templates/bundle-frontend-spa/skills/component-design/references/tailwind-patterns.md +65 -0
- package/templates/bundle-frontend-spa/skills/e2e-testing/SKILL.md +241 -79
- package/templates/bundle-frontend-spa/skills/e2e-testing/references/playwright-selectors.md +66 -0
- package/templates/bundle-frontend-spa/skills/e2e-testing/references/test-patterns.md +82 -0
- package/templates/bundle-frontend-spa/skills/integration-api/SKILL.md +221 -31
- package/templates/bundle-frontend-spa/skills/integration-api/references/api-patterns.md +81 -0
- package/templates/bundle-frontend-spa/skills/react-patterns/SKILL.md +195 -70
- package/templates/bundle-frontend-spa/skills/react-patterns/references/component-checklist.md +22 -0
- package/templates/bundle-frontend-spa/skills/react-patterns/references/hook-patterns.md +63 -0
- package/templates/bundle-frontend-spa/skills/responsive-layout/SKILL.md +162 -22
- package/templates/bundle-frontend-spa/skills/responsive-layout/references/breakpoint-guide.md +63 -0
- package/templates/bundle-frontend-spa/skills/state-management/SKILL.md +158 -30
- package/templates/bundle-frontend-spa/skills/state-management/references/react-query-config.md +64 -0
- package/templates/bundle-frontend-spa/skills/state-management/references/state-patterns.md +78 -0
- package/templates/bundle-jhipster-microservices/skills/ci-cd-pipeline/SKILL.md +135 -45
- package/templates/bundle-jhipster-microservices/skills/ci-cd-pipeline/references/gitlab-ci-templates.md +93 -0
- package/templates/bundle-jhipster-microservices/skills/clean-architecture/SKILL.md +87 -21
- package/templates/bundle-jhipster-microservices/skills/clean-architecture/references/layer-rules.md +78 -0
- package/templates/bundle-jhipster-microservices/skills/ddd-tactical/SKILL.md +94 -25
- package/templates/bundle-jhipster-microservices/skills/ddd-tactical/references/ddd-patterns.md +48 -0
- package/templates/bundle-jhipster-microservices/skills/jhipster-angular/SKILL.md +63 -21
- package/templates/bundle-jhipster-microservices/skills/jhipster-angular/references/angular-microservices.md +40 -0
- package/templates/bundle-jhipster-microservices/skills/jhipster-angular/references/angular-structure.md +59 -0
- package/templates/bundle-jhipster-microservices/skills/jhipster-docker-k8s/SKILL.md +125 -91
- package/templates/bundle-jhipster-microservices/skills/jhipster-docker-k8s/references/docker-k8s-commands.md +68 -0
- package/templates/bundle-jhipster-microservices/skills/jhipster-entities/SKILL.md +72 -20
- package/templates/bundle-jhipster-microservices/skills/jhipster-entities/references/cross-service-entities.md +36 -0
- package/templates/bundle-jhipster-microservices/skills/jhipster-entities/references/jdl-types.md +56 -0
- package/templates/bundle-jhipster-microservices/skills/jhipster-gateway/SKILL.md +80 -8
- package/templates/bundle-jhipster-microservices/skills/jhipster-gateway/references/gateway-config.md +43 -0
- package/templates/bundle-jhipster-microservices/skills/jhipster-kafka/SKILL.md +115 -22
- package/templates/bundle-jhipster-microservices/skills/jhipster-kafka/references/kafka-events.md +39 -0
- package/templates/bundle-jhipster-microservices/skills/jhipster-registry/SKILL.md +92 -23
- package/templates/bundle-jhipster-microservices/skills/jhipster-registry/references/consul-config.md +61 -0
- package/templates/bundle-jhipster-microservices/skills/jhipster-service/SKILL.md +81 -18
- package/templates/bundle-jhipster-microservices/skills/jhipster-service/references/service-patterns.md +40 -0
- package/templates/bundle-jhipster-microservices/skills/testing-strategy/SKILL.md +101 -20
- package/templates/bundle-jhipster-microservices/skills/testing-strategy/references/test-naming.md +55 -0
- package/templates/bundle-jhipster-monorepo/skills/clean-architecture/SKILL.md +87 -21
- package/templates/bundle-jhipster-monorepo/skills/clean-architecture/references/layer-rules.md +78 -0
- package/templates/bundle-jhipster-monorepo/skills/ddd-tactical/SKILL.md +94 -25
- package/templates/bundle-jhipster-monorepo/skills/ddd-tactical/references/ddd-patterns.md +48 -0
- package/templates/bundle-jhipster-monorepo/skills/jhipster-angular/SKILL.md +99 -52
- package/templates/bundle-jhipster-monorepo/skills/jhipster-angular/references/angular-structure.md +59 -0
- package/templates/bundle-jhipster-monorepo/skills/jhipster-entities/SKILL.md +89 -36
- package/templates/bundle-jhipster-monorepo/skills/jhipster-entities/references/jdl-types.md +56 -0
- package/templates/bundle-jhipster-monorepo/skills/jhipster-liquibase/SKILL.md +123 -23
- package/templates/bundle-jhipster-monorepo/skills/jhipster-liquibase/references/liquibase-operations.md +95 -0
- package/templates/bundle-jhipster-monorepo/skills/jhipster-security/SKILL.md +106 -19
- package/templates/bundle-jhipster-monorepo/skills/jhipster-security/references/security-checklist.md +47 -0
- package/templates/bundle-jhipster-monorepo/skills/jhipster-spring/SKILL.md +84 -16
- package/templates/bundle-jhipster-monorepo/skills/jhipster-spring/references/spring-layers.md +41 -0
- package/templates/bundle-jhipster-monorepo/skills/testing-strategy/SKILL.md +101 -20
- package/templates/bundle-jhipster-monorepo/skills/testing-strategy/references/test-naming.md +55 -0
|
@@ -1,54 +1,96 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rag-pipeline
|
|
3
|
-
description:
|
|
3
|
+
description: Build a complete RAG pipeline with document ingestion, chunking, embedding, vector indexing, and hybrid retrieval using LangChain and pgvector. Use when you need to implement semantic search, answer questions over documents, or create a retrieval-augmented generation system.
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
author: Maestro
|
|
4
6
|
---
|
|
5
7
|
|
|
6
8
|
# RAG Pipeline
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
Build production-ready retrieval-augmented generation systems with LangChain, pgvector, and hybrid search.
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
## When to Use
|
|
13
|
+
- User needs to build a Q&A system over internal documents
|
|
14
|
+
- User wants to implement semantic search on a document corpus
|
|
15
|
+
- User needs to ingest and chunk documents for vector indexing
|
|
16
|
+
- User wants hybrid retrieval (semantic + keyword) with re-ranking
|
|
17
|
+
- User needs to set up a pgvector-backed vector store
|
|
18
|
+
|
|
19
|
+
## Available Operations
|
|
20
|
+
1. Ingest documents (Markdown, PDF, text) with LangChain loaders
|
|
21
|
+
2. Split documents into chunks with RecursiveCharacterTextSplitter
|
|
22
|
+
3. Generate embeddings and store in pgvector
|
|
23
|
+
4. Build hybrid retrieval (semantic + BM25 keyword search)
|
|
24
|
+
5. Add Cohere re-ranking for precision
|
|
25
|
+
6. Create a query chain with LLM and source attribution
|
|
26
|
+
|
|
27
|
+
## Multi-Step Workflow
|
|
28
|
+
|
|
29
|
+
### Step 1: Install Dependencies
|
|
30
|
+
```bash
|
|
31
|
+
pip install langchain langchain-openai langchain-community langchain-postgres langchain-cohere pgvector psycopg2-binary unstructured rank-bm25
|
|
12
32
|
```
|
|
13
33
|
|
|
14
|
-
|
|
34
|
+
Set required environment variables:
|
|
35
|
+
```bash
|
|
36
|
+
export OPENAI_API_KEY="sk-..."
|
|
37
|
+
export COHERE_API_KEY="..."
|
|
38
|
+
export DATABASE_URL="postgresql://user:pass@localhost:5432/vectordb"
|
|
39
|
+
```
|
|
15
40
|
|
|
41
|
+
### Step 2: Ingest Documents
|
|
16
42
|
```python
|
|
17
43
|
from langchain_community.document_loaders import DirectoryLoader, UnstructuredMarkdownLoader
|
|
18
|
-
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
|
19
44
|
|
|
20
|
-
#
|
|
45
|
+
# Load all markdown files from a directory
|
|
21
46
|
loader = DirectoryLoader(
|
|
22
47
|
"./documents/",
|
|
23
48
|
glob="**/*.md",
|
|
24
|
-
loader_cls=UnstructuredMarkdownLoader
|
|
49
|
+
loader_cls=UnstructuredMarkdownLoader,
|
|
50
|
+
show_progress=True
|
|
25
51
|
)
|
|
26
52
|
docs = loader.load()
|
|
53
|
+
print(f"Loaded {len(docs)} documents")
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
For PDF files:
|
|
57
|
+
```python
|
|
58
|
+
from langchain_community.document_loaders import PyPDFLoader
|
|
59
|
+
|
|
60
|
+
loader = PyPDFLoader("documents/report.pdf")
|
|
61
|
+
docs = loader.load()
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Step 3: Split into Chunks
|
|
65
|
+
```python
|
|
66
|
+
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
|
27
67
|
|
|
28
|
-
# Splitter com separadores Markdown
|
|
29
68
|
splitter = RecursiveCharacterTextSplitter(
|
|
30
69
|
chunk_size=1000,
|
|
31
70
|
chunk_overlap=200,
|
|
32
71
|
separators=["\n## ", "\n### ", "\n\n", "\n", ". ", " "]
|
|
33
72
|
)
|
|
34
73
|
chunks = splitter.split_documents(docs)
|
|
74
|
+
print(f"Split into {len(chunks)} chunks")
|
|
35
75
|
```
|
|
36
76
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
Cada chunk deve ter:
|
|
77
|
+
### Step 4: Enrich Metadata
|
|
40
78
|
```python
|
|
79
|
+
from datetime import datetime
|
|
80
|
+
|
|
41
81
|
for chunk in chunks:
|
|
42
82
|
chunk.metadata.update({
|
|
43
83
|
"source": chunk.metadata.get("source", "unknown"),
|
|
44
|
-
"doc_type":
|
|
45
|
-
"
|
|
46
|
-
"
|
|
84
|
+
"doc_type": chunk.metadata.get("source", "").split(".")[-1],
|
|
85
|
+
"indexed_at": datetime.now().isoformat(),
|
|
86
|
+
"chunk_size": len(chunk.page_content),
|
|
47
87
|
})
|
|
48
|
-
```
|
|
49
88
|
|
|
50
|
-
|
|
89
|
+
# Verify metadata
|
|
90
|
+
print(chunks[0].metadata)
|
|
91
|
+
```
|
|
51
92
|
|
|
93
|
+
### Step 5: Generate Embeddings and Store in pgvector
|
|
52
94
|
```python
|
|
53
95
|
from langchain_openai import OpenAIEmbeddings
|
|
54
96
|
from langchain_postgres import PGVector
|
|
@@ -61,29 +103,28 @@ vectorstore = PGVector(
|
|
|
61
103
|
embedding_function=embeddings,
|
|
62
104
|
)
|
|
63
105
|
vectorstore.add_documents(chunks)
|
|
106
|
+
print(f"Indexed {len(chunks)} chunks in pgvector")
|
|
64
107
|
```
|
|
65
108
|
|
|
66
|
-
|
|
67
|
-
|
|
109
|
+
### Step 6: Build Hybrid Retriever (Semantic + BM25)
|
|
68
110
|
```python
|
|
69
111
|
from langchain.retrievers import EnsembleRetriever
|
|
70
112
|
from langchain_community.retrievers import BM25Retriever
|
|
71
113
|
|
|
72
|
-
#
|
|
114
|
+
# Semantic retriever
|
|
73
115
|
semantic_retriever = vectorstore.as_retriever(search_kwargs={"k": 20})
|
|
74
116
|
|
|
75
|
-
# Keyword
|
|
117
|
+
# Keyword retriever (BM25)
|
|
76
118
|
bm25_retriever = BM25Retriever.from_documents(chunks, k=20)
|
|
77
119
|
|
|
78
|
-
#
|
|
120
|
+
# Combine with Reciprocal Rank Fusion
|
|
79
121
|
hybrid_retriever = EnsembleRetriever(
|
|
80
122
|
retrievers=[semantic_retriever, bm25_retriever],
|
|
81
|
-
weights=[0.6, 0.4]
|
|
123
|
+
weights=[0.6, 0.4] # favor semantic
|
|
82
124
|
)
|
|
83
125
|
```
|
|
84
126
|
|
|
85
|
-
|
|
86
|
-
|
|
127
|
+
### Step 7: Add Re-Ranking
|
|
87
128
|
```python
|
|
88
129
|
from langchain.retrievers import ContextualCompressionRetriever
|
|
89
130
|
from langchain_cohere import CohereRerank
|
|
@@ -95,34 +136,81 @@ final_retriever = ContextualCompressionRetriever(
|
|
|
95
136
|
)
|
|
96
137
|
```
|
|
97
138
|
|
|
98
|
-
|
|
99
|
-
|
|
139
|
+
### Step 8: Build Query Chain
|
|
100
140
|
```python
|
|
141
|
+
from langchain_openai import ChatOpenAI
|
|
101
142
|
from langchain_core.prompts import ChatPromptTemplate
|
|
102
143
|
from langchain_core.output_parsers import StrOutputParser
|
|
144
|
+
from langchain_core.runnables import RunnablePassthrough
|
|
145
|
+
|
|
146
|
+
llm = ChatOpenAI(model="gpt-4o", temperature=0)
|
|
103
147
|
|
|
104
148
|
prompt = ChatPromptTemplate.from_template("""
|
|
105
|
-
|
|
106
|
-
|
|
149
|
+
Answer the question based only on the provided context.
|
|
150
|
+
If the answer is not in the context, say "I could not find that information."
|
|
107
151
|
|
|
108
|
-
|
|
109
|
-
|
|
152
|
+
Context: {context}
|
|
153
|
+
Question: {question}
|
|
110
154
|
""")
|
|
111
155
|
|
|
156
|
+
def format_docs(docs):
|
|
157
|
+
return "\n\n".join(doc.page_content for doc in docs)
|
|
158
|
+
|
|
112
159
|
chain = (
|
|
113
|
-
{"context": final_retriever, "question": RunnablePassthrough()}
|
|
160
|
+
{"context": final_retriever | format_docs, "question": RunnablePassthrough()}
|
|
114
161
|
| prompt
|
|
115
162
|
| llm
|
|
116
163
|
| StrOutputParser()
|
|
117
164
|
)
|
|
118
165
|
|
|
119
|
-
|
|
166
|
+
# Test the chain
|
|
167
|
+
result = chain.invoke("What is the recommended chunk size for markdown documents?")
|
|
168
|
+
print(result)
|
|
120
169
|
```
|
|
121
170
|
|
|
122
|
-
|
|
171
|
+
### Step 9: Verify Retrieval Quality
|
|
172
|
+
```python
|
|
173
|
+
# Test retrieval with known questions
|
|
174
|
+
test_queries = [
|
|
175
|
+
"How do I set up authentication?",
|
|
176
|
+
"What database should I use?",
|
|
177
|
+
"How do I deploy with Docker?",
|
|
178
|
+
]
|
|
179
|
+
|
|
180
|
+
for query in test_queries:
|
|
181
|
+
docs = final_retriever.invoke(query)
|
|
182
|
+
print(f"\nQuery: {query}")
|
|
183
|
+
print(f"Top result source: {docs[0].metadata.get('source', 'unknown')}")
|
|
184
|
+
print(f"Top result preview: {docs[0].page_content[:200]}...")
|
|
185
|
+
```
|
|
123
186
|
|
|
124
|
-
|
|
125
|
-
-
|
|
126
|
-
-
|
|
127
|
-
|
|
128
|
-
|
|
187
|
+
## Resources
|
|
188
|
+
- `references/chunking-strategies.md` - Guide to chunk sizes, overlap, and separators
|
|
189
|
+
- `references/embedding-models.md` - Comparison of embedding model options
|
|
190
|
+
|
|
191
|
+
## Examples
|
|
192
|
+
### Example 1: Build Q&A Over Documentation
|
|
193
|
+
User asks: "Set up a RAG system to answer questions about our API docs"
|
|
194
|
+
Response approach:
|
|
195
|
+
1. Load docs from the docs/ directory with DirectoryLoader
|
|
196
|
+
2. Split with RecursiveCharacterTextSplitter (1000 chars, 200 overlap)
|
|
197
|
+
3. Embed with OpenAI text-embedding-3-large and store in pgvector
|
|
198
|
+
4. Build hybrid retriever with BM25 fallback
|
|
199
|
+
5. Create query chain with GPT-4o
|
|
200
|
+
6. Test with sample questions and verify source attribution
|
|
201
|
+
|
|
202
|
+
### Example 2: Add Semantic Search to Existing App
|
|
203
|
+
User asks: "Add search to our knowledge base"
|
|
204
|
+
Response approach:
|
|
205
|
+
1. Ingest existing knowledge base documents
|
|
206
|
+
2. Index in pgvector with embeddings
|
|
207
|
+
3. Expose a retriever endpoint that returns top-5 results
|
|
208
|
+
4. Return results with source metadata and relevance scores
|
|
209
|
+
|
|
210
|
+
## Notes
|
|
211
|
+
- Chunk size 800-1200 works well for most text; use 400-600 for code
|
|
212
|
+
- Always include chunk_overlap (15-20% of chunk_size) to preserve context across boundaries
|
|
213
|
+
- Use hybrid retrieval (semantic + BM25) for better recall than either alone
|
|
214
|
+
- Re-ranking is critical for precision -- always add it for production systems
|
|
215
|
+
- Test retrieval quality with a golden dataset of question-answer pairs before deploying
|
|
216
|
+
- Monitor retrieval metrics: precision@k, recall@k, MRR
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Chunking Strategies
|
|
2
|
+
|
|
3
|
+
## Recommended Defaults
|
|
4
|
+
|
|
5
|
+
| Content Type | chunk_size | chunk_overlap | Separators |
|
|
6
|
+
|---|---|---|---|
|
|
7
|
+
| Prose / documentation | 1000 | 200 | `["\n## ", "\n### ", "\n\n", "\n", ". "]` |
|
|
8
|
+
| Code files | 500 | 50 | `["\nclass ", "\ndef ", "\n\n", "\n"]` |
|
|
9
|
+
| Legal / contracts | 1500 | 300 | `["\n\n", "\n", ". "]` |
|
|
10
|
+
| Chat logs / Q&A | 500 | 100 | `["\n\n", "\n"]` |
|
|
11
|
+
|
|
12
|
+
## RecursiveCharacterTextSplitter
|
|
13
|
+
```python
|
|
14
|
+
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
|
15
|
+
|
|
16
|
+
# For markdown docs
|
|
17
|
+
splitter = RecursiveCharacterTextSplitter(
|
|
18
|
+
chunk_size=1000,
|
|
19
|
+
chunk_overlap=200,
|
|
20
|
+
separators=["\n## ", "\n### ", "\n\n", "\n", ". ", " "]
|
|
21
|
+
)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Code Splitting
|
|
25
|
+
```python
|
|
26
|
+
from langchain.text_splitter import Language, RecursiveCharacterTextSplitter
|
|
27
|
+
|
|
28
|
+
python_splitter = RecursiveCharacterTextSplitter.from_language(
|
|
29
|
+
language=Language.PYTHON, chunk_size=500, chunk_overlap=50
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
ts_splitter = RecursiveCharacterTextSplitter.from_language(
|
|
33
|
+
language=Language.TS, chunk_size=500, chunk_overlap=50
|
|
34
|
+
)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Validation
|
|
38
|
+
After chunking, verify:
|
|
39
|
+
1. No chunks are empty or whitespace-only
|
|
40
|
+
2. Average chunk size is close to target
|
|
41
|
+
3. Metadata is preserved on all chunks
|
|
42
|
+
4. Key information is not split across chunk boundaries (spot-check)
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
sizes = [len(c.page_content) for c in chunks]
|
|
46
|
+
print(f"Chunks: {len(chunks)}")
|
|
47
|
+
print(f"Avg size: {sum(sizes)/len(sizes):.0f}")
|
|
48
|
+
print(f"Min: {min(sizes)}, Max: {max(sizes)}")
|
|
49
|
+
empty = [c for c in chunks if len(c.page_content.strip()) == 0]
|
|
50
|
+
print(f"Empty chunks: {len(empty)}")
|
|
51
|
+
```
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Embedding Models Comparison
|
|
2
|
+
|
|
3
|
+
## OpenAI Models
|
|
4
|
+
|
|
5
|
+
| Model | Dimensions | Max Tokens | Best For |
|
|
6
|
+
|---|---|---|---|
|
|
7
|
+
| text-embedding-3-large | 3072 (or custom) | 8191 | Highest quality, production |
|
|
8
|
+
| text-embedding-3-small | 1536 | 8191 | Cost-effective, good quality |
|
|
9
|
+
| text-embedding-ada-002 | 1536 | 8191 | Legacy, still widely used |
|
|
10
|
+
|
|
11
|
+
## Usage with LangChain
|
|
12
|
+
```python
|
|
13
|
+
from langchain_openai import OpenAIEmbeddings
|
|
14
|
+
|
|
15
|
+
# Default (text-embedding-3-large with reduced dimensions)
|
|
16
|
+
embeddings = OpenAIEmbeddings(model="text-embedding-3-large", dimensions=1536)
|
|
17
|
+
|
|
18
|
+
# Full dimensions for maximum quality
|
|
19
|
+
embeddings = OpenAIEmbeddings(model="text-embedding-3-large", dimensions=3072)
|
|
20
|
+
|
|
21
|
+
# Cost-effective option
|
|
22
|
+
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Open Source Alternatives
|
|
26
|
+
```python
|
|
27
|
+
from langchain_community.embeddings import HuggingFaceEmbeddings
|
|
28
|
+
|
|
29
|
+
# All-MiniLM (fast, lightweight)
|
|
30
|
+
embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
|
|
31
|
+
|
|
32
|
+
# BGE (high quality, multilingual)
|
|
33
|
+
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-large-en-v1.5")
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Dimensionality and pgvector
|
|
37
|
+
When creating the pgvector extension and table:
|
|
38
|
+
```sql
|
|
39
|
+
CREATE EXTENSION IF NOT EXISTS vector;
|
|
40
|
+
|
|
41
|
+
-- Match dimensions to your embedding model
|
|
42
|
+
CREATE TABLE embeddings (
|
|
43
|
+
id SERIAL PRIMARY KEY,
|
|
44
|
+
content TEXT,
|
|
45
|
+
embedding vector(1536) -- adjust to match model dimensions
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
CREATE INDEX ON embeddings USING ivfflat (embedding vector_cosine_ops);
|
|
49
|
+
```
|
|
@@ -1,17 +1,43 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: authentication
|
|
3
|
-
description:
|
|
3
|
+
description: Implement JWT authentication with login, refresh tokens, route protection, and Axios interceptors for both frontend and backend. Use when you need to add login, JWT auth, protected routes, or token refresh to an application.
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
author: Maestro
|
|
4
6
|
---
|
|
5
7
|
|
|
6
8
|
# Authentication
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
Implement complete JWT authentication flow covering backend token generation, frontend auth state, Axios interceptors, and protected routes.
|
|
9
11
|
|
|
12
|
+
## When to Use
|
|
13
|
+
- User needs to implement login/logout functionality
|
|
14
|
+
- User wants to protect routes requiring authentication
|
|
15
|
+
- User needs JWT token generation and validation (backend)
|
|
16
|
+
- User wants to add automatic token refresh
|
|
17
|
+
- User needs to set up Axios interceptors for auth headers
|
|
18
|
+
|
|
19
|
+
## Available Operations
|
|
20
|
+
1. Create JWT token generation and validation (FastAPI backend)
|
|
21
|
+
2. Build login/logout flow with Zustand auth store
|
|
22
|
+
3. Set up Axios interceptors for automatic Bearer token headers
|
|
23
|
+
4. Implement protected route wrappers
|
|
24
|
+
5. Add refresh token rotation
|
|
25
|
+
|
|
26
|
+
## Multi-Step Workflow
|
|
27
|
+
|
|
28
|
+
### Step 1: Install Backend Dependencies
|
|
29
|
+
```bash
|
|
30
|
+
pip install fastapi python-jose[cryptography] passlib[bcrypt] python-multipart
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Step 2: Create JWT Token Utilities (Backend)
|
|
10
34
|
```python
|
|
35
|
+
# src/auth/jwt.py
|
|
36
|
+
import os
|
|
37
|
+
from datetime import datetime, timedelta
|
|
38
|
+
from jose import jwt, JWTError
|
|
11
39
|
from fastapi import Depends, HTTPException, status
|
|
12
40
|
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
|
13
|
-
from jose import jwt, JWTError
|
|
14
|
-
from datetime import datetime, timedelta
|
|
15
41
|
|
|
16
42
|
security = HTTPBearer()
|
|
17
43
|
SECRET_KEY = os.environ["JWT_SECRET"]
|
|
@@ -23,7 +49,7 @@ def create_access_token(user_id: str) -> str:
|
|
|
23
49
|
payload = {
|
|
24
50
|
"sub": user_id,
|
|
25
51
|
"exp": datetime.utcnow() + ACCESS_TOKEN_EXPIRE,
|
|
26
|
-
"type": "access"
|
|
52
|
+
"type": "access",
|
|
27
53
|
}
|
|
28
54
|
return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
|
|
29
55
|
|
|
@@ -31,11 +57,13 @@ def create_refresh_token(user_id: str) -> str:
|
|
|
31
57
|
payload = {
|
|
32
58
|
"sub": user_id,
|
|
33
59
|
"exp": datetime.utcnow() + REFRESH_TOKEN_EXPIRE,
|
|
34
|
-
"type": "refresh"
|
|
60
|
+
"type": "refresh",
|
|
35
61
|
}
|
|
36
62
|
return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
|
|
37
63
|
|
|
38
|
-
async def get_current_user(
|
|
64
|
+
async def get_current_user(
|
|
65
|
+
credentials: HTTPAuthorizationCredentials = Depends(security),
|
|
66
|
+
) -> User:
|
|
39
67
|
try:
|
|
40
68
|
payload = jwt.decode(credentials.credentials, SECRET_KEY, algorithms=[ALGORITHM])
|
|
41
69
|
if payload.get("type") != "access":
|
|
@@ -46,22 +74,68 @@ async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(s
|
|
|
46
74
|
return user
|
|
47
75
|
except JWTError:
|
|
48
76
|
raise HTTPException(status_code=401, detail="Invalid token")
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Step 3: Create Auth Endpoints (Backend)
|
|
80
|
+
```python
|
|
81
|
+
# src/auth/routes.py
|
|
82
|
+
from fastapi import APIRouter, HTTPException
|
|
83
|
+
|
|
84
|
+
router = APIRouter(prefix="/auth", tags=["auth"])
|
|
85
|
+
|
|
86
|
+
@router.post("/login")
|
|
87
|
+
async def login(data: LoginRequest):
|
|
88
|
+
user = await user_repo.find_by_email(data.email)
|
|
89
|
+
if not user or not verify_password(data.password, user.hashed_password):
|
|
90
|
+
raise HTTPException(status_code=401, detail="Invalid credentials")
|
|
91
|
+
return {
|
|
92
|
+
"access_token": create_access_token(str(user.id)),
|
|
93
|
+
"refresh_token": create_refresh_token(str(user.id)),
|
|
94
|
+
"user": UserResponse.from_entity(user),
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@router.post("/refresh")
|
|
98
|
+
async def refresh(data: RefreshRequest):
|
|
99
|
+
try:
|
|
100
|
+
payload = jwt.decode(data.refresh_token, SECRET_KEY, algorithms=[ALGORITHM])
|
|
101
|
+
if payload.get("type") != "refresh":
|
|
102
|
+
raise HTTPException(status_code=401, detail="Invalid token type")
|
|
103
|
+
return {"access_token": create_access_token(payload["sub"])}
|
|
104
|
+
except JWTError:
|
|
105
|
+
raise HTTPException(status_code=401, detail="Invalid refresh token")
|
|
49
106
|
|
|
50
|
-
# Endpoint protegido
|
|
51
107
|
@router.get("/me")
|
|
52
108
|
async def get_me(user: User = Depends(get_current_user)):
|
|
53
109
|
return UserResponse.from_entity(user)
|
|
54
110
|
```
|
|
55
111
|
|
|
56
|
-
|
|
112
|
+
Test the backend:
|
|
113
|
+
```bash
|
|
114
|
+
# Start the API server
|
|
115
|
+
uvicorn src.main:app --reload --port 8000
|
|
57
116
|
|
|
117
|
+
# Test login
|
|
118
|
+
curl -X POST http://localhost:8000/auth/login -H "Content-Type: application/json" -d '{"email": "admin@example.com", "password": "admin123"}'
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Step 4: Install Frontend Dependencies
|
|
122
|
+
```bash
|
|
123
|
+
npm install axios zustand
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Step 5: Create Auth Store (Frontend)
|
|
58
127
|
```tsx
|
|
128
|
+
// src/stores/useAuthStore.ts
|
|
129
|
+
import { create } from 'zustand';
|
|
130
|
+
import { authApi } from '@/services/authApi';
|
|
131
|
+
|
|
59
132
|
interface AuthState {
|
|
60
133
|
token: string | null;
|
|
61
134
|
user: User | null;
|
|
135
|
+
isAuthenticated: boolean;
|
|
62
136
|
login: (email: string, password: string) => Promise<void>;
|
|
63
137
|
logout: () => void;
|
|
64
|
-
|
|
138
|
+
setToken: (token: string) => void;
|
|
65
139
|
}
|
|
66
140
|
|
|
67
141
|
export const useAuthStore = create<AuthState>((set) => ({
|
|
@@ -70,21 +144,130 @@ export const useAuthStore = create<AuthState>((set) => ({
|
|
|
70
144
|
isAuthenticated: !!localStorage.getItem('token'),
|
|
71
145
|
|
|
72
146
|
login: async (email, password) => {
|
|
73
|
-
const { access_token, user } = await authApi.login(email, password);
|
|
147
|
+
const { access_token, refresh_token, user } = await authApi.login(email, password);
|
|
74
148
|
localStorage.setItem('token', access_token);
|
|
149
|
+
localStorage.setItem('refresh_token', refresh_token);
|
|
75
150
|
set({ token: access_token, user, isAuthenticated: true });
|
|
76
151
|
},
|
|
77
152
|
|
|
78
153
|
logout: () => {
|
|
79
154
|
localStorage.removeItem('token');
|
|
155
|
+
localStorage.removeItem('refresh_token');
|
|
80
156
|
set({ token: null, user: null, isAuthenticated: false });
|
|
81
157
|
},
|
|
158
|
+
|
|
159
|
+
setToken: (token) => {
|
|
160
|
+
localStorage.setItem('token', token);
|
|
161
|
+
set({ token });
|
|
162
|
+
},
|
|
82
163
|
}));
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Step 6: Set Up Axios Interceptors
|
|
167
|
+
```tsx
|
|
168
|
+
// src/lib/api.ts
|
|
169
|
+
import axios from 'axios';
|
|
170
|
+
import { useAuthStore } from '@/stores/useAuthStore';
|
|
83
171
|
|
|
84
|
-
|
|
172
|
+
export const api = axios.create({
|
|
173
|
+
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:8000/api/v1',
|
|
174
|
+
timeout: 10000,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Add auth header to every request
|
|
85
178
|
api.interceptors.request.use((config) => {
|
|
86
179
|
const token = localStorage.getItem('token');
|
|
87
|
-
if (token)
|
|
180
|
+
if (token) {
|
|
181
|
+
config.headers.Authorization = `Bearer ${token}`;
|
|
182
|
+
}
|
|
88
183
|
return config;
|
|
89
184
|
});
|
|
185
|
+
|
|
186
|
+
// Handle 401 responses: try refresh, then logout
|
|
187
|
+
api.interceptors.response.use(
|
|
188
|
+
(response) => response.data,
|
|
189
|
+
async (error) => {
|
|
190
|
+
if (error.response?.status === 401) {
|
|
191
|
+
const refreshToken = localStorage.getItem('refresh_token');
|
|
192
|
+
if (refreshToken && !error.config._retry) {
|
|
193
|
+
error.config._retry = true;
|
|
194
|
+
try {
|
|
195
|
+
const { access_token } = await authApi.refresh(refreshToken);
|
|
196
|
+
useAuthStore.getState().setToken(access_token);
|
|
197
|
+
error.config.headers.Authorization = `Bearer ${access_token}`;
|
|
198
|
+
return api(error.config);
|
|
199
|
+
} catch {
|
|
200
|
+
useAuthStore.getState().logout();
|
|
201
|
+
window.location.href = '/login';
|
|
202
|
+
}
|
|
203
|
+
} else {
|
|
204
|
+
useAuthStore.getState().logout();
|
|
205
|
+
window.location.href = '/login';
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return Promise.reject(error.response?.data || error);
|
|
209
|
+
}
|
|
210
|
+
);
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Step 7: Create Protected Route Wrapper
|
|
214
|
+
```tsx
|
|
215
|
+
// src/components/ProtectedRoute.tsx
|
|
216
|
+
import { Navigate } from 'react-router-dom';
|
|
217
|
+
import { useAuthStore } from '@/stores/useAuthStore';
|
|
218
|
+
|
|
219
|
+
export function ProtectedRoute({ children }: { children: React.ReactNode }) {
|
|
220
|
+
const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
|
|
221
|
+
|
|
222
|
+
if (!isAuthenticated) {
|
|
223
|
+
return <Navigate to="/login" replace />;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return <>{children}</>;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Usage in router:
|
|
230
|
+
// <Route path="/dashboard" element={<ProtectedRoute><Dashboard /></ProtectedRoute>} />
|
|
90
231
|
```
|
|
232
|
+
|
|
233
|
+
### Step 8: Verify the Auth Flow
|
|
234
|
+
```bash
|
|
235
|
+
npm run dev
|
|
236
|
+
# 1. Navigate to /login
|
|
237
|
+
# 2. Enter credentials and submit
|
|
238
|
+
# 3. Verify redirect to /dashboard
|
|
239
|
+
# 4. Verify protected routes redirect to /login when not authenticated
|
|
240
|
+
# 5. Verify token is in localStorage
|
|
241
|
+
# 6. Verify API calls include Authorization header (check Network tab)
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Resources
|
|
245
|
+
- `references/jwt-security.md` - JWT security best practices and common vulnerabilities
|
|
246
|
+
|
|
247
|
+
## Examples
|
|
248
|
+
### Example 1: Add Login to an Existing App
|
|
249
|
+
User asks: "Add authentication to our React app with a login page"
|
|
250
|
+
Response approach:
|
|
251
|
+
1. Create backend auth endpoints (login, refresh, me)
|
|
252
|
+
2. Create useAuthStore with Zustand
|
|
253
|
+
3. Set up Axios interceptors for token management
|
|
254
|
+
4. Create LoginPage component with form
|
|
255
|
+
5. Wrap routes in ProtectedRoute
|
|
256
|
+
6. Test the full flow end-to-end
|
|
257
|
+
|
|
258
|
+
### Example 2: Fix Token Expiry Issues
|
|
259
|
+
User asks: "Users keep getting logged out, how do I add token refresh?"
|
|
260
|
+
Response approach:
|
|
261
|
+
1. Add refresh token endpoint to backend
|
|
262
|
+
2. Store refresh token in localStorage alongside access token
|
|
263
|
+
3. Add 401 interceptor that tries refresh before logging out
|
|
264
|
+
4. Set retry flag to prevent infinite refresh loops
|
|
265
|
+
5. Test by setting short access token expiry and verifying refresh works
|
|
266
|
+
|
|
267
|
+
## Notes
|
|
268
|
+
- Never store tokens in cookies without httpOnly + secure + sameSite flags
|
|
269
|
+
- Set a reasonable access token expiry (1 hour) and longer refresh token (7 days)
|
|
270
|
+
- Always validate token type ("access" vs "refresh") server-side
|
|
271
|
+
- Use environment variables for JWT_SECRET, never hardcode
|
|
272
|
+
- Implement rate limiting on login endpoints to prevent brute force
|
|
273
|
+
- Clear all stored tokens on logout
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# JWT Security Best Practices
|
|
2
|
+
|
|
3
|
+
## Token Storage
|
|
4
|
+
| Method | Security | XSS Risk | CSRF Risk |
|
|
5
|
+
|---|---|---|---|
|
|
6
|
+
| localStorage | Low | High | None |
|
|
7
|
+
| httpOnly Cookie | High | None | Medium |
|
|
8
|
+
| In-memory (variable) | Highest | None | None |
|
|
9
|
+
|
|
10
|
+
Recommendation: Use httpOnly cookies for production, localStorage for development/SPAs.
|
|
11
|
+
|
|
12
|
+
## Token Expiry
|
|
13
|
+
| Token Type | Recommended Expiry |
|
|
14
|
+
|---|---|
|
|
15
|
+
| Access Token | 15 min - 1 hour |
|
|
16
|
+
| Refresh Token | 7 - 30 days |
|
|
17
|
+
|
|
18
|
+
## Security Checklist
|
|
19
|
+
- [ ] Use strong secret key (256+ bits): `openssl rand -hex 32`
|
|
20
|
+
- [ ] Set algorithm explicitly (HS256 or RS256), never "none"
|
|
21
|
+
- [ ] Validate token type (access vs refresh) on every endpoint
|
|
22
|
+
- [ ] Implement refresh token rotation (new refresh token on each use)
|
|
23
|
+
- [ ] Rate limit login endpoint (5 attempts per minute)
|
|
24
|
+
- [ ] Log failed login attempts
|
|
25
|
+
- [ ] Clear all tokens on logout (both client and server)
|
|
26
|
+
- [ ] Use HTTPS in production
|
|
27
|
+
|
|
28
|
+
## Common Vulnerabilities
|
|
29
|
+
1. **Algorithm confusion**: Always specify algorithm in `jwt.decode()`, never accept from token
|
|
30
|
+
2. **Missing expiry check**: Always include `exp` claim
|
|
31
|
+
3. **Token reuse after logout**: Maintain a blocklist or use short-lived tokens
|
|
32
|
+
4. **Refresh without rotation**: Rotate refresh tokens to limit damage from stolen tokens
|
|
33
|
+
|
|
34
|
+
## Generating a Secure Secret
|
|
35
|
+
```bash
|
|
36
|
+
# Generate a 256-bit hex secret
|
|
37
|
+
openssl rand -hex 32
|
|
38
|
+
|
|
39
|
+
# Or with Python
|
|
40
|
+
python -c "import secrets; print(secrets.token_hex(32))"
|
|
41
|
+
```
|