Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,16 @@ ZAI_MODEL=GLM-4.7
# DESCRIPTION: Max concurrent in-flight Z.AI requests.
ZAI_MAX_CONCURRENT=2

# ------------------------------------------------------------------------------
# Baidu Qianfan (ERNIE)
# ------------------------------------------------------------------------------
# DESCRIPTION: Baidu Qianfan API key (format: bce-v3/ALTAK-...).
# BAIDU_API_KEY=your-baidu-qianfan-api-key
# DESCRIPTION: Qianfan v2 OpenAI-compatible chat completions endpoint.
BAIDU_ENDPOINT=https://qianfan.baidubce.com/v2/chat/completions
# DESCRIPTION: Default Baidu ERNIE model.
BAIDU_MODEL=ernie-4.5-turbo-128k

# ------------------------------------------------------------------------------
# Codex (uses your ChatGPT subscription via local codex CLI)
# ------------------------------------------------------------------------------
Expand Down
41 changes: 3 additions & 38 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,37 +27,7 @@ This directory contains GitHub Actions workflows for automated testing and CI/CD

---

### 2. Web Tools Tests (`web-tools-tests.yml`)

**Purpose:** Run web search tool tests when related files change

**Triggers:**
- Changes to web tools source files:
- `src/tools/web.js`
- `src/tools/web-client.js`
- `src/clients/retry.js`
- `src/config/index.js`
- `test/web-tools.test.js`

**What it does:**
- Runs only the web tools test suite
- Generates test summary in GitHub Actions UI
- Faster feedback for web tools changes

**Test Coverage:**
- HTML extraction (9 tests)
- HTTP keep-alive agent (2 tests)
- Retry logic with exponential backoff (2 tests)
- Configuration management (3 tests)
- Error handling (1 test)
- Performance validation (1 test)
- Body preview configuration (1 test)

**Total:** 19 tests

---

### 3. NPM Publish (`npm-publish.yml`)
### 2. NPM Publish (`npm-publish.yml`)

**Purpose:** Automatically publish package to npm registry

Expand All @@ -81,7 +51,7 @@ This directory contains GitHub Actions workflows for automated testing and CI/CD

---

### 4. Version Bump (`version-bump.yml`)
### 3. Version Bump (`version-bump.yml`)

**Purpose:** Manual workflow to bump version and create releases

Expand All @@ -104,7 +74,7 @@ This directory contains GitHub Actions workflows for automated testing and CI/CD

---

### 5. IndexNow Notification (`index.yml`)
### 4. IndexNow Notification (`index.yml`)

**Purpose:** Notify search engines when documentation is updated

Expand All @@ -124,7 +94,6 @@ Add these badges to your README.md:

```markdown
![CI Tests](https://github.com/vishalveerareddy123/Lynkr/actions/workflows/ci.yml/badge.svg)
![Web Tools Tests](https://github.com/vishalveerareddy123/Lynkr/actions/workflows/web-tools-tests.yml/badge.svg)
![npm version](https://img.shields.io/npm/v/lynkr.svg)
![npm downloads](https://img.shields.io/npm/dt/lynkr.svg)
```
Expand All @@ -137,10 +106,6 @@ Before pushing, run tests locally:
# Run all unit tests
npm run test:unit

# Run only web tools tests
DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com \
node --test test/web-tools.test.js

# Run quick tests (routing only)
npm run test:quick

Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ jobs:
node --test \
test/routing.test.js \
test/hybrid-routing-integration.test.js \
test/web-tools.test.js \
test/passthrough-mode.test.js \
test/openrouter-error-resilience.test.js \
test/format-conversion.test.js \
Expand All @@ -53,6 +52,5 @@ jobs:
test/telemetry-routing.test.js \
test/distill.test.js \
test/large-payload.test.js \
test/code-mode.test.js \
test/prompt-cache-injection.test.js
timeout-minutes: 5
56 changes: 0 additions & 56 deletions .github/workflows/web-tools-tests.yml

This file was deleted.

5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,11 @@ Claude Code / Cursor / Codex / Cline / Continue
| **OpenAI** | Cloud | GPT-4o, o3-mini | $$$ |
| **Atlas Cloud** | Cloud | Qwen, DeepSeek, and other OpenAI-compatible models | $-$$$ |
| **DeepSeek** | Cloud | DeepSeek R1, Reasoner | $ |
| **Z.ai** | Cloud | GLM-4.7, GLM-4.5-Air | $ |
| **Moonshot AI** | Cloud | Kimi K2.6, Kimi K3 | $ |
| **Baidu Qianfan** | Cloud | ERNIE 4.5 Turbo, ERNIE X1.1 | $ (unverified — not yet probed against a live key) |

**4 local providers** for 100% offline, free usage. **11+ cloud providers** for scale.
**4 local providers** for 100% offline, free usage. **14+ cloud providers** for scale.

<div align="center">
<a href="https://www.atlascloud.ai/?utm_source=github&amp;utm_medium=readme&amp;utm_campaign=lynkr">
Expand Down
14 changes: 13 additions & 1 deletion bin/lynkr-init.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,21 @@ const PROVIDERS = {
extras: [],
defaultModel: 'kimi-k2-turbo-preview',
},
baidu: {
label: 'Baidu Qianfan (ERNIE)',
local: false,
creds: [
{ key: 'BAIDU_API_KEY', label: 'Baidu Qianfan API key (bce-v3/ALTAK-...)', secret: true },
],
extras: [],
defaultModel: 'ernie-4.5-turbo-128k',
},
};

const PROVIDER_ORDER = [
'ollama', 'llamacpp', 'lmstudio',
'azure-anthropic', 'azure-openai', 'openai', 'atlas', 'openrouter', 'edenai',
'databricks', 'bedrock', 'vertex', 'zai', 'moonshot',
'databricks', 'bedrock', 'vertex', 'zai', 'moonshot', 'baidu',
];
const TIERS = ['SIMPLE', 'MEDIUM', 'COMPLEX', 'REASONING'];

Expand Down Expand Up @@ -330,6 +339,9 @@ const BASELINE_ENV = {
MOONSHOT_API_KEY: '',
MOONSHOT_ENDPOINT: 'https://api.moonshot.ai/v1/chat/completions',
MOONSHOT_MODEL: 'kimi-k2.6',
BAIDU_API_KEY: '',
BAIDU_ENDPOINT: 'https://qianfan.baidubce.com/v2/chat/completions',
BAIDU_MODEL: 'ernie-4.5-turbo-128k',
LLAMACPP_ENDPOINT: 'http://localhost:8080',
LLAMACPP_MODEL: 'default',
LLAMACPP_TIMEOUT_MS: '120000',
Expand Down
66 changes: 65 additions & 1 deletion documentation/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -963,6 +963,69 @@ curl -X POST http://localhost:8000/v1/chat/completions -H "Content-Type: applica

---

### 12. Baidu Qianfan / ERNIE (OpenAI-Compatible)

**Best for:** Chinese-market deployments, ERNIE reasoning models, OpenAI-compatible API

> **Note:** model roster and sampling-param behavior below are best-effort from public docs and have not yet been probed against a live key — treat as a starting point, not a confirmed-stable config.

#### Configuration

```env
MODEL_PROVIDER=baidu
BAIDU_API_KEY=bce-v3/ALTAK-your-baidu-qianfan-api-key
BAIDU_ENDPOINT=https://qianfan.baidubce.com/v2/chat/completions
BAIDU_MODEL=ernie-4.5-turbo-128k
```

#### Getting a Baidu Qianfan API Key

1. Visit [Baidu AI Cloud Qianfan console](https://console.bce.baidu.com/qianfan/)
2. Sign up or log in (Baidu Cloud account)
3. Create an API key under the v2 / OpenAI-compatible access section — keys are formatted `bce-v3/ALTAK-...`
4. Add credits to your account

#### Available Models

```env
BAIDU_MODEL=ernie-4.5-turbo-128k # General-purpose, 128K context (recommended default)
BAIDU_MODEL=ernie-x1.1 # Reasoning model
BAIDU_MODEL=ernie-speed-8k # Smaller/cheaper, fast responses
```

#### How It Works

Qianfan's v2 endpoint is an **OpenAI-compatible** chat completions API. Lynkr handles all format conversion automatically:

1. Claude Code CLI sends Anthropic-format request to Lynkr
2. Lynkr converts Anthropic messages → OpenAI chat completions format
3. Request is sent to Qianfan's `/v2/chat/completions` endpoint
4. Qianfan response is converted back to Anthropic format
5. Claude Code CLI receives a standard Anthropic response

#### Important Notes

- **Streaming:** Buffered only for now — `baidu` is not yet in the SSE-transform provider list, pending confirmation that Qianfan's stream shape matches OpenAI's exactly.
- **Tool Calling:** Tool calling support via OpenAI function calling format (automatically converted from Anthropic format) — not yet exercised against a live key.
- **System Messages:** Qianfan's v2 endpoint supports the `system` role natively.

#### Benefits

- ✅ **OpenAI-compatible** — Standard chat completions API, drop-in wiring
- ✅ **Reasoning model available** — `ernie-x1.1`
- ✅ **System role support** — Native system message handling

#### Test Connection

```bash
curl -X POST https://qianfan.baidubce.com/v2/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $BAIDU_API_KEY" \
-d '{"model":"ernie-4.5-turbo-128k","messages":[{"role":"user","content":"Hello"}]}'
```

---

## Tier-Based Routing & Fallback

### Intelligent 4-Tier Routing
Expand Down Expand Up @@ -1025,7 +1088,7 @@ AZURE_OPENAI_API_KEY=your-key

| Variable | Description | Default |
|----------|-------------|---------|
| `MODEL_PROVIDER` | Primary provider (`databricks`, `bedrock`, `openrouter`, `ollama`, `llamacpp`, `azure-openai`, `azure-anthropic`, `openai`, `lmstudio`, `zai`, `moonshot`, `vertex`) | `databricks` |
| `MODEL_PROVIDER` | Primary provider (`databricks`, `bedrock`, `openrouter`, `ollama`, `llamacpp`, `azure-openai`, `azure-anthropic`, `openai`, `lmstudio`, `zai`, `moonshot`, `vertex`, `baidu`) | `databricks` |
| `PORT` | HTTP port for proxy server | `8081` |
| `WORKSPACE_ROOT` | Workspace directory path | `process.cwd()` |
| `LOG_LEVEL` | Logging level (`error`, `warn`, `info`, `debug`) | `info` |
Expand Down Expand Up @@ -1071,6 +1134,7 @@ _* Tool calling only supported by Claude models on Bedrock_
| **OpenAI** | GPT-4o | $2.50 | $10.00 |
| **Azure OpenAI** | GPT-4o | $2.50 | $10.00 |
| **Moonshot** | Kimi K2 Turbo | See moonshot.ai | See moonshot.ai |
| **Baidu Qianfan** | ERNIE 4.5 Turbo | See qianfan.baidubce.com | See qianfan.baidubce.com |
| **Ollama** | Any model | **FREE** | **FREE** |
| **llama.cpp** | Any model | **FREE** | **FREE** |
| **LM Studio** | Any model | **FREE** | **FREE** |
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "lynkr",
"version": "9.10.0",
"version": "9.12.0",
"files": [
"index.js",
"install.sh",
Expand Down Expand Up @@ -37,7 +37,7 @@
"dev": "nodemon index.js",
"lint": "eslint src index.js",
"test": "npm run test:unit && npm run test:performance",
"test:unit": "DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com LOG_FILE_ENABLED=false node --test test/routing.test.js test/hybrid-routing-integration.test.js test/retry-logic.test.js test/sse-transformer.test.js test/passthrough-stream.test.js test/passthrough-mode.test.js test/openrouter-error-resilience.test.js test/format-conversion.test.js test/azure-openai-config.test.js test/azure-openai-format-conversion.test.js test/azure-openai-routing.test.js test/azure-openai-streaming.test.js test/azure-openai-error-resilience.test.js test/azure-openai-integration.test.js test/openai-integration.test.js test/atlas-integration.test.js test/toon-compression.test.js test/gcf-compression.test.js test/llamacpp-integration.test.js test/resilience.test.js test/telemetry-routing.test.js test/memory/store.test.js test/memory/surprise.test.js test/memory/extractor.test.js test/memory/search.test.js test/memory/retriever.test.js test/memory/distiller.test.js test/memory/distiller-freeze.test.js test/memory/wiki.test.js test/memory/skills-cache.test.js test/memory/tencentdb-launcher.test.js test/distill.test.js test/large-payload.test.js test/prompt-cache-injection.test.js test/risk-analyzer.test.js test/interaction-block.test.js test/preflight.test.js test/token-reduction.test.js test/session-affinity.test.js test/cache-state.test.js test/cache-switch-cost.test.js test/lens-recommendations.test.js test/model-registry-cost.test.js test/output-format-guard.test.js test/tier-fallback.test.js test/wrap.test.js test/init.test.js test/tool-call-response-metadata.test.js test/degradation.test.js test/routing-telemetry-columns.test.js test/sticky-routing.test.js test/knn-ambiguous-escalate.test.js test/deescalator.test.js test/client-profiles.test.js test/strip-internal-fields.test.js test/complexity-tool-subtraction.test.js test/bandit.test.js test/routing-propensity.test.js test/reward-pipeline.test.js test/knn-cold-start.test.js test/calibration.test.js test/feedback-loop.test.js test/session-fingerprint.test.js test/side-request-guards.test.js test/verifier.test.js test/intent-score.test.js test/difficulty-classifier.test.js test/classifier-setup.test.js test/usage-stats.test.js test/loop-guard.test.js test/moonshot-model-mapping.test.js",
"test:unit": "DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com LOG_FILE_ENABLED=false node --test test/routing.test.js test/hybrid-routing-integration.test.js test/retry-logic.test.js test/sse-transformer.test.js test/passthrough-stream.test.js test/passthrough-mode.test.js test/openrouter-error-resilience.test.js test/format-conversion.test.js test/azure-openai-config.test.js test/azure-openai-format-conversion.test.js test/azure-openai-routing.test.js test/azure-openai-streaming.test.js test/azure-openai-error-resilience.test.js test/azure-openai-integration.test.js test/openai-integration.test.js test/atlas-integration.test.js test/toon-compression.test.js test/gcf-compression.test.js test/llamacpp-integration.test.js test/resilience.test.js test/telemetry-routing.test.js test/memory/store.test.js test/memory/surprise.test.js test/memory/extractor.test.js test/memory/search.test.js test/memory/retriever.test.js test/memory/distiller.test.js test/memory/distiller-freeze.test.js test/memory/wiki.test.js test/memory/skills-cache.test.js test/memory/tencentdb-launcher.test.js test/distill.test.js test/large-payload.test.js test/prompt-cache-injection.test.js test/risk-analyzer.test.js test/interaction-block.test.js test/preflight.test.js test/token-reduction.test.js test/session-affinity.test.js test/cache-state.test.js test/cache-switch-cost.test.js test/lens-recommendations.test.js test/model-registry-cost.test.js test/output-format-guard.test.js test/tier-fallback.test.js test/wrap.test.js test/init.test.js test/tool-call-response-metadata.test.js test/degradation.test.js test/routing-telemetry-columns.test.js test/sticky-routing.test.js test/knn-ambiguous-escalate.test.js test/deescalator.test.js test/client-profiles.test.js test/strip-internal-fields.test.js test/complexity-tool-subtraction.test.js test/bandit.test.js test/routing-propensity.test.js test/reward-pipeline.test.js test/knn-cold-start.test.js test/calibration.test.js test/feedback-loop.test.js test/session-fingerprint.test.js test/side-request-guards.test.js test/verifier.test.js test/intent-score.test.js test/difficulty-classifier.test.js test/classifier-setup.test.js test/usage-stats.test.js test/loop-guard.test.js test/moonshot-model-mapping.test.js test/baidu-model-mapping.test.js",
"test:memory": "DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node --test test/memory/store.test.js test/memory/surprise.test.js test/memory/extractor.test.js test/memory/search.test.js test/memory/retriever.test.js test/memory/distiller.test.js test/memory/distiller-freeze.test.js test/memory/wiki.test.js test/memory/skills-cache.test.js test/memory/tencentdb-launcher.test.js",
"test:new-features": "DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node --test test/passthrough-mode.test.js test/openrouter-error-resilience.test.js test/format-conversion.test.js",
"test:performance": "DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node test/hybrid-routing-performance.test.js && DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node test/performance-tests.js",
Expand Down
12 changes: 12 additions & 0 deletions src/api/openai-router.js
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,18 @@ function getConfiguredProviders() {
});
}

if (config.baidu?.apiKey) {
providers.push({
name: "baidu",
type: "baidu-qianfan",
models: [
config.baidu.model || "ernie-4.5-turbo-128k",
"ernie-4.5-turbo-128k",
"ernie-x1.1"
]
});
}

if (config.vertex?.projectId) {
providers.push({
name: "vertex",
Expand Down
15 changes: 15 additions & 0 deletions src/api/providers-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,21 @@ function getConfiguredProviders() {
});
}

// Check Baidu Qianfan (ERNIE)
if (config.baidu?.apiKey) {
providers.push({
name: "baidu",
type: "baidu-qianfan",
baseUrl: config.baidu.endpoint || "https://qianfan.baidubce.com/v2/chat/completions",
enabled: true,
models: [
{ id: config.baidu.model || "ernie-4.5-turbo-128k", name: "Configured Model" },
{ id: "ernie-4.5-turbo-128k", name: "ERNIE 4.5 Turbo 128K" },
{ id: "ernie-x1.1", name: "ERNIE X1.1 (reasoning)" },
]
});
}

// Check Vertex AI (Google Cloud)
if (config.vertex?.projectId) {
const region = config.vertex.region || "us-east5";
Expand Down
Loading
Loading