@gitlab/gitlab-ai-provider
v3.0.8
Published
GitLab Duo provider for Vercel AI SDK
Readme
GitLab AI Provider
A comprehensive TypeScript provider for integrating GitLab Duo AI capabilities with the Vercel AI SDK. This package enables seamless access to GitLab's AI-powered features including chat, agentic workflows, and tool calling through a unified interface.
🌟 Features
- 🤖 Agentic Chat: Native tool calling support via GitLab's Anthropic proxy
- 🔐 Multiple Authentication: Support for OAuth, Personal Access Tokens, and OpenCode auth
- 🌐 Self-Hosted Support: Works with both GitLab.com and self-hosted instances
- 📦 Tool Executors: Built-in Anthropic and GitLab API tool executors
- 🔍 Project Detection: Automatic GitLab project detection from git remotes
- 💾 Smart Caching: Project and token caching for optimal performance
- 🎯 Type-Safe: Complete TypeScript definitions with Zod validation
📦 Installation
npm install @gitlab/gitlab-ai-providerPeer Dependencies
npm install @ai-sdk/provider @ai-sdk/provider-utils🚀 Quick Start
Basic Chat
import { createGitLab } from '@gitlab/gitlab-ai-provider';
import { generateText } from 'ai';
const gitlab = createGitLab({
apiKey: process.env.GITLAB_TOKEN,
instanceUrl: 'https://gitlab.com', // optional, defaults to gitlab.com
});
// All equivalent ways to create a chat model:
const model = gitlab('duo-chat'); // callable provider
const model2 = gitlab.chat('duo-chat'); // .chat() alias (recommended)
const model3 = gitlab.languageModel('duo-chat'); // explicit method
const { text } = await generateText({
model: gitlab.chat('duo-chat'),
prompt: 'Explain how to create a merge request in GitLab',
});
console.log(text);Agentic Chat with Tool Calling
import { createGitLab } from '@gitlab/gitlab-ai-provider';
import { generateText } from 'ai';
const gitlab = createGitLab({
apiKey: process.env.GITLAB_TOKEN,
});
// Use agentic model for native tool calling support
const model = gitlab.agenticChat('duo-chat', {
anthropicModel: 'claude-sonnet-4-20250514',
maxTokens: 8192,
});
const { text } = await generateText({
model,
prompt: 'List all open merge requests in my project',
tools: {
// Your custom tools here
},
});Agentic Chat with Feature Flags
You can pass feature flags to enable experimental features in GitLab's Anthropic proxy:
import { createGitLab } from '@gitlab/gitlab-ai-provider';
// Option 1: Set feature flags globally for all agentic chat models
const gitlab = createGitLab({
apiKey: process.env.GITLAB_TOKEN,
featureFlags: {
duo_agent_platform_agentic_chat: true,
duo_agent_platform: true,
},
});
const model = gitlab.agenticChat('duo-chat');
// Option 2: Set feature flags per model (overrides global flags)
const modelWithFlags = gitlab.agenticChat('duo-chat', {
featureFlags: {
duo_agent_platform_agentic_chat: true,
duo_agent_platform: true,
custom_feature_flag: false,
},
});
// Option 3: Merge both (model-level flags take precedence)
const gitlab2 = createGitLab({
featureFlags: {
duo_agent_platform: true, // will be overridden
},
});
const mergedModel = gitlab2.agenticChat('duo-chat', {
featureFlags: {
duo_agent_platform: false, // overrides provider-level
duo_agent_platform_agentic_chat: true, // adds new flag
},
});🔑 Authentication
Personal Access Token
const gitlab = createGitLab({
apiKey: 'glpat-xxxxxxxxxxxxxxxxxxxx',
});Environment Variable
export GITLAB_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxxconst gitlab = createGitLab(); // Automatically uses GITLAB_TOKENOAuth (OpenCode Auth)
The provider automatically detects and uses OpenCode authentication if available:
const gitlab = createGitLab({
instanceUrl: 'https://gitlab.com',
// OAuth tokens are loaded from ~/.opencode/auth.json
});Custom Headers
const gitlab = createGitLab({
apiKey: 'your-token',
headers: {
'X-Custom-Header': 'value',
},
});🏗️ Architecture
Core Components
1. GitLabProvider
Main provider factory that creates language models with different capabilities.
interface GitLabProvider {
(modelId: string): LanguageModelV2;
languageModel(modelId: string): LanguageModelV2;
agenticChat(modelId: string, options?: GitLabAgenticOptions): GitLabAgenticLanguageModel;
}2. GitLabAgenticLanguageModel
Provides native tool calling through GitLab's Anthropic proxy.
- Uses Claude models via
https://cloud.gitlab.com/ai/v1/proxy/anthropic/ - Automatic token refresh and retry logic
- Direct access token management
- Supports all Anthropic tool calling features
Tool Executors
AnthropicToolExecutor
Executes local file system and command tools:
list_dir- List directory contentsread_file- Read file contentswrite_file- Write to filesedit_file- Edit file with find/replacefind_files- Find files by patternmkdir- Create directoriesgrep- Search file contentsrun_command- Execute shell commandsrun_git_command- Execute git commands
GitLabApiToolExecutor
Executes GitLab API operations:
- Merge Requests: get, list, changes, discussions, notes
- Issues: get, list, notes
- Pipelines: list, get, jobs, logs
- Repository: files, commits, branches
- Search: global and project search
- Projects: get, members
Supporting Utilities
GitLabProjectDetector
Automatically detects GitLab projects from git remotes.
const detector = new GitLabProjectDetector({
instanceUrl: 'https://gitlab.com',
getHeaders: () => ({ Authorization: `Bearer ${token}` }),
});
const project = await detector.detectProject(process.cwd());
// Returns: { id: 12345, path: 'group/project', namespaceId: 67890 }GitLabProjectCache
Caches project information with TTL.
const cache = new GitLabProjectCache(5 * 60 * 1000); // 5 minutes
cache.set('key', project);
const cached = cache.get('key');GitLabOAuthManager
Manages OAuth token lifecycle.
const oauthManager = new GitLabOAuthManager();
// Exchange authorization code
const tokens = await oauthManager.exchangeAuthorizationCode({
instanceUrl: 'https://gitlab.com',
code: 'auth-code',
codeVerifier: 'verifier',
});
// Refresh tokens
const refreshed = await oauthManager.refreshIfNeeded(tokens);GitLabDirectAccessClient
Manages direct access tokens for Anthropic proxy.
const client = new GitLabDirectAccessClient({
instanceUrl: 'https://gitlab.com',
getHeaders: () => ({ Authorization: `Bearer ${token}` }),
});
const directToken = await client.getDirectAccessToken();
// Returns: { token: 'xxx', headers: {...}, expiresAt: 123456 }📚 API Reference
Provider Configuration
interface GitLabProviderSettings {
instanceUrl?: string; // Default: 'https://gitlab.com'
apiKey?: string; // PAT or OAuth access token
refreshToken?: string; // OAuth refresh token
name?: string; // Provider name prefix
headers?: Record<string, string>; // Custom headers
fetch?: typeof fetch; // Custom fetch implementation
}Agentic Chat Options
interface GitLabAgenticOptions {
anthropicModel?: string; // Default: 'claude-sonnet-4-20250514'
maxTokens?: number; // Default: 8192
}Error Handling
import { GitLabError } from '@gitlab/gitlab-ai-provider';
try {
const result = await generateText({ model, prompt });
} catch (error) {
if (error instanceof GitLabError) {
if (error.isAuthError()) {
console.error('Authentication failed');
} else if (error.isRateLimitError()) {
console.error('Rate limit exceeded');
} else if (error.isServerError()) {
console.error('Server error:', error.statusCode);
}
}
}🔧 Development
Build
npm run build # Build once
npm run build:watch # Build in watch modeTesting
npm test # Run all tests
npm run test:watch # Run tests in watch modeCode Quality
npm run lint # Lint code
npm run lint:fix # Lint and auto-fix
npm run format # Format code
npm run format:check # Check formatting
npm run type-check # TypeScript type checkingProject Structure
gitlab-ai-provider/
├── src/
│ ├── index.ts # Main exports
│ ├── gitlab-provider.ts # Provider factory
│ ├── gitlab-agentic-language-model.ts # Agentic chat model
│ ├── gitlab-direct-access.ts # Direct access tokens
│ ├── gitlab-oauth-manager.ts # OAuth management
│ ├── gitlab-oauth-types.ts # OAuth types
│ ├── gitlab-project-detector.ts # Project detection
│ ├── gitlab-project-cache.ts # Project caching
│ ├── gitlab-anthropic-tools.ts # Anthropic tool executor
│ ├── gitlab-api-tools.ts # GitLab API tool executor
│ ├── gitlab-api-types.ts # API types
│ ├── gitlab-error.ts # Error handling
│ └── gitlab-workflow-debug.ts # Debug logging
├── tests/ # Test files
├── dist/ # Build output
├── package.json
├── tsconfig.json
├── tsup.config.ts
└── vitest.config.ts📝 Code Style
- Imports: Named imports, organized by external → internal → types
- Formatting: Single quotes, semicolons, 100 char line width, 2 space indent
- Types: Interfaces for public APIs, Zod schemas for runtime validation
- Naming: camelCase (variables/functions), PascalCase (classes/types), kebab-case (files)
- Exports: Named exports only (no default exports)
- Comments: JSDoc for public APIs with @param/@returns
Assistant
🤝 Contributing
Contributions are welcome! Please see our Contributing Guide for detailed guidelines on:
- Code style and conventions
- Development workflow
- Testing requirements
- Submitting merge requests
- Developer Certificate of Origin and License
Quick Start for Contributors:
Commit Messages: Use conventional commits format
feat(scope): add new feature fix(scope): fix bug docs(scope): update documentationCode Quality: Ensure all checks pass
npm run lint npm run type-check npm testTesting: Add tests for new features
🔗 Links
🙏 Acknowledgments
This project is built on top of:
Made with ❤️ for the OpenCode community
