Skip to content

Commit b94bf59

Browse files
committed
Merge PR zilliztech#186 from zilliztech/claude-context
2 parents 960b7ac + fdf16df commit b94bf59

File tree

11 files changed

+477
-21
lines changed

11 files changed

+477
-21
lines changed

README.md

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,23 @@ Copy your Personal Key to replace `your-zilliz-cloud-api-key` in the configurati
4242
</details>
4343

4444
<details>
45-
<summary>Get OpenAI API Key for embedding model</summary>
45+
<summary>Get an API Key for embedding model</summary>
4646

47-
You need an OpenAI API key for the embedding model. You can get one by signing up at [OpenAI](https://platform.openai.com/api-keys).
47+
You need an API key for the embedding model. Claude Context supports multiple providers:
4848

49-
Your API key will look like this: it always starts with `sk-`.
50-
Copy your key and use it in the configuration examples below as `your-openai-api-key`.
49+
**Option 1: OpenAI**
50+
- Sign up at [OpenAI](https://platform.openai.com/api-keys)
51+
- Your API key will start with `sk-`
52+
- Use as `your-openai-api-key` in configuration
53+
54+
**Option 2: Azure OpenAI**
55+
- Use your Azure OpenAI resource endpoint and API key
56+
- Requires deployment name instead of model name
57+
- See [Azure OpenAI Documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/)
58+
59+
**Option 3: Other Providers**
60+
- VoyageAI, Gemini, or Ollama (local)
61+
- See [Provider Configuration Guide](packages/mcp/README.md#embedding-provider-configuration) for details
5162

5263
</details>
5364

@@ -542,8 +553,7 @@ Claude Context is a monorepo containing three main packages:
542553
- **`@zilliz/claude-context-mcp`**: Model Context Protocol server for AI agent integration
543554

544555
### Supported Technologies
545-
546-
- **Embedding Providers**: [OpenAI](https://openai.com), [VoyageAI](https://voyageai.com), [Ollama](https://ollama.ai), [Gemini](https://gemini.google.com)
556+
- **Embedding Providers**: [OpenAI](https://openai.com), [Azure OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service), [VoyageAI](https://voyageai.com), [Ollama](https://ollama.ai), [Gemini](https://gemini.google.com)
547557
- **Vector Databases**: [Milvus](https://milvus.io) or [Zilliz Cloud](https://zilliz.com/cloud)(fully managed vector database as a service)
548558
- **Code Splitters**: AST-based splitter (with automatic fallback), LangChain character-based splitter
549559
- **Languages**: TypeScript, JavaScript, Python, Java, C++, C#, Go, Rust, PHP, Ruby, Swift, Kotlin, Scala, Markdown

docs/getting-started/environment-variables.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,14 @@ Claude Context supports a global configuration file at `~/.context/.env` to simp
2020
### Embedding Provider
2121
| Variable | Description | Default |
2222
|----------|-------------|---------|
23-
| `EMBEDDING_PROVIDER` | Provider: `OpenAI`, `VoyageAI`, `Gemini`, `Ollama` | `OpenAI` |
23+
| `EMBEDDING_PROVIDER` | Provider: `OpenAI`, `AzureOpenAI`, `VoyageAI`, `Gemini`, `Ollama` | `OpenAI` |
2424
| `EMBEDDING_MODEL` | Embedding model name (works for all providers) | Provider-specific default |
2525
| `OPENAI_API_KEY` | OpenAI API key | Required for OpenAI |
2626
| `OPENAI_BASE_URL` | OpenAI API base URL (optional, for custom endpoints) | `https://api.openai.com/v1` |
27+
| `AZURE_OPENAI_API_KEY` | Azure OpenAI API key | Required for Azure OpenAI |
28+
| `AZURE_OPENAI_ENDPOINT` | Azure OpenAI endpoint URL | Required for Azure OpenAI |
29+
| `AZURE_OPENAI_DEPLOYMENT_NAME` | Azure deployment name | Required for Azure OpenAI |
30+
| `AZURE_OPENAI_API_VERSION` | Azure API version | `2024-02-01` |
2731
| `VOYAGEAI_API_KEY` | VoyageAI API key | Required for VoyageAI |
2832
| `GEMINI_API_KEY` | Gemini API key | Required for Gemini |
2933
| `GEMINI_BASE_URL` | Gemini API base URL (optional, for custom endpoints) | `https://generativelanguage.googleapis.com/v1beta` |
@@ -33,6 +37,8 @@ Claude Context supports a global configuration file at `~/.context/.env` to simp
3337
> **Supported Model Names:**
3438
>
3539
> - OpenAI Models: See `getSupportedModels` in [`openai-embedding.ts`](https://github.com/zilliztech/claude-context/blob/master/packages/core/src/embedding/openai-embedding.ts) for the full list of supported models.
40+
>
41+
> - Azure OpenAI: Uses deployment names instead of model names. Supports the same models as OpenAI (text-embedding-3-small, text-embedding-3-large, text-embedding-ada-002).
3642
>
3743
> - VoyageAI Models: See `getSupportedModels` in [`voyageai-embedding.ts`](https://github.com/zilliztech/claude-context/blob/master/packages/core/src/embedding/voyageai-embedding.ts) for the full list of supported models.
3844
>
@@ -67,6 +73,8 @@ Claude Context supports a global configuration file at `~/.context/.env` to simp
6773
## 🚀 Quick Setup
6874

6975
### 1. Create Global Config
76+
77+
**Option A: OpenAI**
7078
```bash
7179
mkdir -p ~/.context
7280
cat > ~/.context/.env << 'EOF'
@@ -77,6 +85,18 @@ MILVUS_TOKEN=your-zilliz-cloud-api-key
7785
EOF
7886
```
7987

88+
**Option B: Azure OpenAI**
89+
```bash
90+
mkdir -p ~/.context
91+
cat > ~/.context/.env << 'EOF'
92+
EMBEDDING_PROVIDER=AzureOpenAI
93+
AZURE_OPENAI_API_KEY=your-azure-api-key
94+
AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com
95+
AZURE_OPENAI_DEPLOYMENT_NAME=text-embedding-3-small-deployment
96+
MILVUS_TOKEN=your-zilliz-cloud-api-key
97+
EOF
98+
```
99+
80100
See the [Example File](../../.env.example) for more details.
81101

82102
### 2. Simplified MCP Configuration

packages/core/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ results.forEach(result => {
105105
## Embedding Providers
106106

107107
- **OpenAI Embeddings** (`text-embedding-3-small`, `text-embedding-3-large`, `text-embedding-ada-002`)
108+
- **Azure OpenAI Embeddings** - Same models as OpenAI, deployed on Azure infrastructure
108109
- **VoyageAI Embeddings** - High-quality embeddings optimized for code (`voyage-code-3`, `voyage-3.5`, etc.)
109110
- **Gemini Embeddings** - Google's embedding models (`gemini-embedding-001`)
110111
- **Ollama Embeddings** - Local embedding models via Ollama
@@ -190,6 +191,30 @@ interface SemanticSearchResult {
190191

191192
## Examples
192193

194+
### Using Azure OpenAI Embeddings
195+
196+
```typescript
197+
import { Context, MilvusVectorDatabase, AzureOpenAIEmbedding } from '@zilliz/claude-context-core';
198+
199+
// Initialize with Azure OpenAI embedding provider
200+
const embedding = new AzureOpenAIEmbedding({
201+
deploymentName: 'text-embedding-3-small-deployment',
202+
apiKey: process.env.AZURE_OPENAI_API_KEY || 'your-azure-api-key',
203+
azureEndpoint: process.env.AZURE_OPENAI_ENDPOINT || 'https://your-resource.openai.azure.com',
204+
apiVersion: '2024-02-01' // Optional
205+
});
206+
207+
const vectorDatabase = new MilvusVectorDatabase({
208+
address: process.env.MILVUS_ADDRESS || 'localhost:19530',
209+
token: process.env.MILVUS_TOKEN || ''
210+
});
211+
212+
const context = new Context({
213+
embedding,
214+
vectorDatabase
215+
});
216+
```
217+
193218
### Using VoyageAI Embeddings
194219

195220
```typescript
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
import { AzureOpenAI } from 'openai';
2+
import { Embedding, EmbeddingVector } from './base-embedding';
3+
4+
export interface AzureOpenAIEmbeddingConfig {
5+
deploymentName: string; // Azure deployment name (not model name)
6+
apiKey: string; // Azure OpenAI API key
7+
azureEndpoint: string; // Required: Azure endpoint URL
8+
apiVersion?: string; // Optional: defaults to stable version
9+
}
10+
11+
export class AzureOpenAIEmbedding extends Embedding {
12+
private client: AzureOpenAI;
13+
private config: AzureOpenAIEmbeddingConfig;
14+
private dimension: number = 1536; // Default dimension for text-embedding-3-small
15+
protected maxTokens: number = 8192; // Maximum tokens for OpenAI embedding models
16+
17+
constructor(config: AzureOpenAIEmbeddingConfig) {
18+
super();
19+
this.config = config;
20+
21+
// Validate endpoint format
22+
if (!config.azureEndpoint.startsWith('https://')) {
23+
throw new Error('Azure OpenAI endpoint must start with https://');
24+
}
25+
26+
// Initialize Azure OpenAI client with API key authentication
27+
this.client = new AzureOpenAI({
28+
apiKey: config.apiKey,
29+
apiVersion: config.apiVersion || '2024-02-01', // Use stable version
30+
endpoint: config.azureEndpoint,
31+
});
32+
}
33+
34+
async detectDimension(testText: string = "test"): Promise<number> {
35+
const knownModels = AzureOpenAIEmbedding.getSupportedModels();
36+
37+
// Try to infer from deployment name if it matches known patterns
38+
// Azure deployment names often include the model name with dashes
39+
for (const [modelName, info] of Object.entries(knownModels)) {
40+
// Check if deployment name contains model pattern (with dashes instead of dots)
41+
const modelPattern = modelName.replace(/\./g, '-');
42+
if (this.config.deploymentName.toLowerCase().includes(modelPattern)) {
43+
return info.dimension;
44+
}
45+
}
46+
47+
// Dynamic detection via API call for custom deployments
48+
try {
49+
const processedText = this.preprocessText(testText);
50+
const response = await this.client.embeddings.create({
51+
model: this.config.deploymentName, // Use deployment name
52+
input: processedText,
53+
encoding_format: 'float',
54+
});
55+
return response.data[0].embedding.length;
56+
} catch (error) {
57+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
58+
59+
// Re-throw authentication errors
60+
if (errorMessage.includes('API key') || errorMessage.includes('unauthorized') || errorMessage.includes('authentication')) {
61+
throw new Error(`Azure OpenAI authentication failed: ${errorMessage}`);
62+
}
63+
64+
// Handle deployment not found errors
65+
if (errorMessage.includes('deployment') || errorMessage.includes('not found')) {
66+
throw new Error(`Azure OpenAI deployment '${this.config.deploymentName}' not found: ${errorMessage}`);
67+
}
68+
69+
throw new Error(`Failed to detect dimension for Azure deployment ${this.config.deploymentName}: ${errorMessage}`);
70+
}
71+
}
72+
73+
async embed(text: string): Promise<EmbeddingVector> {
74+
const processedText = this.preprocessText(text);
75+
76+
// Check if we need to detect dimension
77+
const knownModels = AzureOpenAIEmbedding.getSupportedModels();
78+
let needsDimensionDetection = true;
79+
80+
for (const [modelName, info] of Object.entries(knownModels)) {
81+
const modelPattern = modelName.replace(/\./g, '-');
82+
if (this.config.deploymentName.toLowerCase().includes(modelPattern)) {
83+
this.dimension = info.dimension;
84+
needsDimensionDetection = false;
85+
break;
86+
}
87+
}
88+
89+
if (needsDimensionDetection && this.dimension === 1536) {
90+
// Only detect if we haven't already and are using default
91+
this.dimension = await this.detectDimension();
92+
}
93+
94+
try {
95+
const response = await this.client.embeddings.create({
96+
model: this.config.deploymentName, // Use deployment name
97+
input: processedText,
98+
encoding_format: 'float',
99+
});
100+
101+
// Update dimension from actual response
102+
this.dimension = response.data[0].embedding.length;
103+
104+
return {
105+
vector: response.data[0].embedding,
106+
dimension: this.dimension
107+
};
108+
} catch (error) {
109+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
110+
111+
// Provide specific error messages for common Azure issues
112+
if (errorMessage.includes('API key') || errorMessage.includes('unauthorized')) {
113+
throw new Error(`Azure OpenAI authentication failed: ${errorMessage}`);
114+
}
115+
116+
if (errorMessage.includes('deployment') || errorMessage.includes('not found')) {
117+
throw new Error(`Azure OpenAI deployment '${this.config.deploymentName}' not found: ${errorMessage}`);
118+
}
119+
120+
if (errorMessage.includes('rate limit') || errorMessage.includes('quota')) {
121+
throw new Error(`Azure OpenAI rate limit exceeded: ${errorMessage}`);
122+
}
123+
124+
throw new Error(`Failed to generate Azure OpenAI embedding: ${errorMessage}`);
125+
}
126+
}
127+
128+
async embedBatch(texts: string[]): Promise<EmbeddingVector[]> {
129+
const processedTexts = this.preprocessTexts(texts);
130+
131+
// Check if we need to detect dimension
132+
const knownModels = AzureOpenAIEmbedding.getSupportedModels();
133+
let needsDimensionDetection = true;
134+
135+
for (const [modelName, info] of Object.entries(knownModels)) {
136+
const modelPattern = modelName.replace(/\./g, '-');
137+
if (this.config.deploymentName.toLowerCase().includes(modelPattern)) {
138+
this.dimension = info.dimension;
139+
needsDimensionDetection = false;
140+
break;
141+
}
142+
}
143+
144+
if (needsDimensionDetection && this.dimension === 1536) {
145+
this.dimension = await this.detectDimension();
146+
}
147+
148+
try {
149+
const response = await this.client.embeddings.create({
150+
model: this.config.deploymentName, // Use deployment name
151+
input: processedTexts,
152+
encoding_format: 'float',
153+
});
154+
155+
// Update dimension from actual response
156+
this.dimension = response.data[0].embedding.length;
157+
158+
return response.data.map((item) => ({
159+
vector: item.embedding,
160+
dimension: this.dimension
161+
}));
162+
} catch (error) {
163+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
164+
165+
// Provide specific error messages for common Azure issues
166+
if (errorMessage.includes('API key') || errorMessage.includes('unauthorized')) {
167+
throw new Error(`Azure OpenAI authentication failed: ${errorMessage}`);
168+
}
169+
170+
if (errorMessage.includes('deployment') || errorMessage.includes('not found')) {
171+
throw new Error(`Azure OpenAI deployment '${this.config.deploymentName}' not found: ${errorMessage}`);
172+
}
173+
174+
if (errorMessage.includes('rate limit') || errorMessage.includes('quota')) {
175+
throw new Error(`Azure OpenAI rate limit exceeded: ${errorMessage}`);
176+
}
177+
178+
throw new Error(`Failed to generate Azure OpenAI batch embeddings: ${errorMessage}`);
179+
}
180+
}
181+
182+
getDimension(): number {
183+
// Check if deployment name matches known models
184+
const knownModels = AzureOpenAIEmbedding.getSupportedModels();
185+
186+
for (const [modelName, info] of Object.entries(knownModels)) {
187+
const modelPattern = modelName.replace(/\./g, '-');
188+
if (this.config.deploymentName.toLowerCase().includes(modelPattern)) {
189+
return info.dimension;
190+
}
191+
}
192+
193+
// For custom deployments, return the current dimension
194+
// Note: This may be incorrect until detectDimension() is called
195+
console.warn(`[AzureOpenAIEmbedding] ⚠️ getDimension() called for deployment '${this.config.deploymentName}' - returning ${this.dimension}. Call detectDimension() first for accurate dimension.`);
196+
return this.dimension;
197+
}
198+
199+
getProvider(): string {
200+
return 'Azure OpenAI';
201+
}
202+
203+
/**
204+
* Set deployment name
205+
* @param deploymentName Azure deployment name
206+
*/
207+
async setDeployment(deploymentName: string): Promise<void> {
208+
this.config.deploymentName = deploymentName;
209+
210+
// Check if this matches a known model
211+
const knownModels = AzureOpenAIEmbedding.getSupportedModels();
212+
let foundKnownModel = false;
213+
214+
for (const [modelName, info] of Object.entries(knownModels)) {
215+
const modelPattern = modelName.replace(/\./g, '-');
216+
if (deploymentName.toLowerCase().includes(modelPattern)) {
217+
this.dimension = info.dimension;
218+
foundKnownModel = true;
219+
break;
220+
}
221+
}
222+
223+
if (!foundKnownModel) {
224+
// Detect dimension for custom deployment
225+
this.dimension = await this.detectDimension();
226+
}
227+
}
228+
229+
/**
230+
* Get client instance (for advanced usage)
231+
*/
232+
getClient(): AzureOpenAI {
233+
return this.client;
234+
}
235+
236+
/**
237+
* Get list of supported models (these are OpenAI model names, not Azure deployment names)
238+
* Azure deployments can be named anything, but often include the model name
239+
*/
240+
static getSupportedModels(): Record<string, { dimension: number; description: string }> {
241+
return {
242+
'text-embedding-3-small': {
243+
dimension: 1536,
244+
description: 'High performance and cost-effective embedding model (recommended)'
245+
},
246+
'text-embedding-3-large': {
247+
dimension: 3072,
248+
description: 'Highest performance embedding model with larger dimensions'
249+
},
250+
'text-embedding-ada-002': {
251+
dimension: 1536,
252+
description: 'Legacy model (use text-embedding-3-small instead)'
253+
}
254+
};
255+
}
256+
}

packages/core/src/embedding/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export * from './base-embedding';
33

44
// Implementation class exports
55
export * from './openai-embedding';
6+
export * from './azure-openai-embedding';
67
export * from './voyageai-embedding';
78
export * from './ollama-embedding';
89
export * from './gemini-embedding';

0 commit comments

Comments
 (0)