mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
e304dce40e
- Replace config/config.example.json with V3 format (version: 3, api_keys array, channel_list) - Update config-versioning.md: version 2→3, ConfigV2→ConfigV3, CurrentVersion=3 - Update 7 project READMEs: api_key→api_keys, add version: 3 to quick-start examples - Update 12 security docs (ANTIGRAVITY_AUTH + credential_encryption): api_key→api_keys - Update provider-refactoring.md: api_key→api_keys in all config examples - Update security_configuration.md: api_key→api_keys in Before example - Update 3 channel docs: channels→channel_list in JSON examples
810 lines
23 KiB
Markdown
810 lines
23 KiB
Markdown
> Retour au [README](../project/README.fr.md)
|
|
|
|
# Guide d'authentification et d'intégration Antigravity
|
|
|
|
## Aperçu
|
|
|
|
**Antigravity** (Google Cloud Code Assist) est un fournisseur de modèles IA soutenu par Google qui offre l'accès à des modèles tels que Claude Opus 4.6 et Gemini via l'infrastructure cloud de Google. Ce document fournit un guide complet sur le fonctionnement de l'authentification, la récupération des modèles et l'implémentation d'un nouveau fournisseur dans PicoClaw.
|
|
|
|
---
|
|
|
|
## Table des matières
|
|
|
|
1. [Flux d'authentification](#flux-dauthentification)
|
|
2. [Détails de l'implémentation OAuth](#détails-de-limplémentation-oauth)
|
|
3. [Gestion des jetons](#gestion-des-jetons)
|
|
4. [Récupération de la liste des modèles](#récupération-de-la-liste-des-modèles)
|
|
5. [Suivi de l'utilisation](#suivi-de-lutilisation)
|
|
6. [Structure du plugin fournisseur](#structure-du-plugin-fournisseur)
|
|
7. [Exigences d'intégration](#exigences-dintégration)
|
|
8. [Points de terminaison API](#points-de-terminaison-api)
|
|
9. [Configuration](#configuration)
|
|
10. [Créer un nouveau fournisseur dans PicoClaw](#créer-un-nouveau-fournisseur-dans-picoclaw)
|
|
|
|
---
|
|
|
|
## Flux d'authentification
|
|
|
|
### 1. OAuth 2.0 avec PKCE
|
|
|
|
Antigravity utilise **OAuth 2.0 avec PKCE (Proof Key for Code Exchange)** pour une authentification sécurisée :
|
|
|
|
```
|
|
┌─────────────┐ ┌─────────────────┐
|
|
│ Client │ ───(1) Generate PKCE Pair────────> │ │
|
|
│ │ ───(2) Open Auth URL─────────────> │ Google OAuth │
|
|
│ │ │ Server │
|
|
│ │ <──(3) Redirect with Code───────── │ │
|
|
│ │ └─────────────────┘
|
|
│ │ ───(4) Exchange Code for Tokens──> │ Token URL │
|
|
│ │ │ │
|
|
│ │ <──(5) Access + Refresh Tokens──── │ │
|
|
└─────────────┘ └─────────────────┘
|
|
```
|
|
|
|
### 2. Étapes détaillées
|
|
|
|
#### Étape 1 : Générer les paramètres PKCE
|
|
```typescript
|
|
function generatePkce(): { verifier: string; challenge: string } {
|
|
const verifier = randomBytes(32).toString("hex");
|
|
const challenge = createHash("sha256").update(verifier).digest("base64url");
|
|
return { verifier, challenge };
|
|
}
|
|
```
|
|
|
|
#### Étape 2 : Construire l'URL d'autorisation
|
|
```typescript
|
|
const AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
const REDIRECT_URI = "http://localhost:51121/oauth-callback";
|
|
|
|
function buildAuthUrl(params: { challenge: string; state: string }): string {
|
|
const url = new URL(AUTH_URL);
|
|
url.searchParams.set("client_id", CLIENT_ID);
|
|
url.searchParams.set("response_type", "code");
|
|
url.searchParams.set("redirect_uri", REDIRECT_URI);
|
|
url.searchParams.set("scope", SCOPES.join(" "));
|
|
url.searchParams.set("code_challenge", params.challenge);
|
|
url.searchParams.set("code_challenge_method", "S256");
|
|
url.searchParams.set("state", params.state);
|
|
url.searchParams.set("access_type", "offline");
|
|
url.searchParams.set("prompt", "consent");
|
|
return url.toString();
|
|
}
|
|
```
|
|
|
|
**Portées requises :**
|
|
```typescript
|
|
const SCOPES = [
|
|
"https://www.googleapis.com/auth/cloud-platform",
|
|
"https://www.googleapis.com/auth/userinfo.email",
|
|
"https://www.googleapis.com/auth/userinfo.profile",
|
|
"https://www.googleapis.com/auth/cclog",
|
|
"https://www.googleapis.com/auth/experimentsandconfigs",
|
|
];
|
|
```
|
|
|
|
#### Étape 3 : Gérer le callback OAuth
|
|
|
|
**Mode automatique (développement local) :**
|
|
- Démarrer un serveur HTTP local sur le port 51121
|
|
- Attendre la redirection de Google
|
|
- Extraire le code d'autorisation des paramètres de requête
|
|
|
|
**Mode manuel (distant/sans interface graphique) :**
|
|
- Afficher l'URL d'autorisation à l'utilisateur
|
|
- L'utilisateur complète l'authentification dans son navigateur
|
|
- L'utilisateur colle l'URL de redirection complète dans le terminal
|
|
- Analyser le code depuis l'URL collée
|
|
|
|
#### Étape 4 : Échanger le code contre des jetons
|
|
```typescript
|
|
const TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
|
|
async function exchangeCode(params: {
|
|
code: string;
|
|
verifier: string;
|
|
}): Promise<{ access: string; refresh: string; expires: number }> {
|
|
const response = await fetch(TOKEN_URL, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
body: new URLSearchParams({
|
|
client_id: CLIENT_ID,
|
|
client_secret: CLIENT_SECRET,
|
|
code: params.code,
|
|
grant_type: "authorization_code",
|
|
redirect_uri: REDIRECT_URI,
|
|
code_verifier: params.verifier,
|
|
}),
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
return {
|
|
access: data.access_token,
|
|
refresh: data.refresh_token,
|
|
expires: Date.now() + data.expires_in * 1000 - 5 * 60 * 1000, // 5 min buffer
|
|
};
|
|
}
|
|
```
|
|
|
|
#### Étape 5 : Récupérer les données utilisateur supplémentaires
|
|
|
|
**E-mail de l'utilisateur :**
|
|
```typescript
|
|
async function fetchUserEmail(accessToken: string): Promise<string | undefined> {
|
|
const response = await fetch(
|
|
"https://www.googleapis.com/oauth2/v1/userinfo?alt=json",
|
|
{ headers: { Authorization: `Bearer ${accessToken}` } }
|
|
);
|
|
const data = await response.json();
|
|
return data.email;
|
|
}
|
|
```
|
|
|
|
**ID du projet (requis pour les appels API) :**
|
|
```typescript
|
|
async function fetchProjectId(accessToken: string): Promise<string> {
|
|
const headers = {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
"Content-Type": "application/json",
|
|
"User-Agent": "google-api-nodejs-client/9.15.1",
|
|
"X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
|
|
"Client-Metadata": JSON.stringify({
|
|
ideType: "IDE_UNSPECIFIED",
|
|
platform: "PLATFORM_UNSPECIFIED",
|
|
pluginType: "GEMINI",
|
|
}),
|
|
};
|
|
|
|
const response = await fetch(
|
|
"https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist",
|
|
{
|
|
method: "POST",
|
|
headers,
|
|
body: JSON.stringify({
|
|
metadata: {
|
|
ideType: "IDE_UNSPECIFIED",
|
|
platform: "PLATFORM_UNSPECIFIED",
|
|
pluginType: "GEMINI",
|
|
},
|
|
}),
|
|
}
|
|
);
|
|
|
|
const data = await response.json();
|
|
return data.cloudaicompanionProject || "rising-fact-p41fc"; // Valeur par défaut
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Détails de l'implémentation OAuth
|
|
|
|
### Identifiants client
|
|
|
|
**Important :** Ceux-ci sont encodés en base64 dans le code source pour la synchronisation avec pi-ai :
|
|
|
|
```typescript
|
|
const decode = (s: string) => Buffer.from(s, "base64").toString();
|
|
|
|
const CLIENT_ID = decode(
|
|
"MTA3MTAwNjA2MDU5MS10bWhzc2luMmgyMWxjcmUyMzV2dG9sb2poNGc0MDNlcC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbQ=="
|
|
);
|
|
const CLIENT_SECRET = decode("R09DU1BYLUs1OEZXUjQ4NkxkTEoxbUxCOHNYQzR6NnFEQWY=");
|
|
```
|
|
|
|
### Modes de flux OAuth
|
|
|
|
1. **Flux automatique** (machines locales avec navigateur) :
|
|
- Ouvre le navigateur automatiquement
|
|
- Le serveur de callback local capture la redirection
|
|
- Aucune interaction utilisateur requise après l'authentification initiale
|
|
|
|
2. **Flux manuel** (distant/sans interface/WSL2) :
|
|
- URL affichée pour copier-coller manuellement
|
|
- L'utilisateur complète l'authentification dans un navigateur externe
|
|
- L'utilisateur colle l'URL de redirection complète
|
|
|
|
```typescript
|
|
function shouldUseManualOAuthFlow(isRemote: boolean): boolean {
|
|
return isRemote || isWSL2Sync();
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Gestion des jetons
|
|
|
|
### Structure du profil d'authentification
|
|
|
|
```typescript
|
|
type OAuthCredential = {
|
|
type: "oauth";
|
|
provider: "google-antigravity";
|
|
access: string; // Jeton d'accès
|
|
refresh: string; // Jeton de rafraîchissement
|
|
expires: number; // Horodatage d'expiration (ms depuis epoch)
|
|
email?: string; // E-mail de l'utilisateur
|
|
projectId?: string; // ID du projet Google Cloud
|
|
};
|
|
```
|
|
|
|
### Rafraîchissement des jetons
|
|
|
|
Les identifiants incluent un jeton de rafraîchissement qui peut être utilisé pour obtenir de nouveaux jetons d'accès lorsque le jeton actuel expire. L'expiration est définie avec un tampon de 5 minutes pour éviter les conditions de concurrence.
|
|
|
|
---
|
|
|
|
## Récupération de la liste des modèles
|
|
|
|
### Récupérer les modèles disponibles
|
|
|
|
```typescript
|
|
const BASE_URL = "https://cloudcode-pa.googleapis.com";
|
|
|
|
async function fetchAvailableModels(
|
|
accessToken: string,
|
|
projectId: string
|
|
): Promise<Model[]> {
|
|
const headers = {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
"Content-Type": "application/json",
|
|
"User-Agent": "antigravity",
|
|
"X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
|
|
};
|
|
|
|
const response = await fetch(
|
|
`${BASE_URL}/v1internal:fetchAvailableModels`,
|
|
{
|
|
method: "POST",
|
|
headers,
|
|
body: JSON.stringify({ project: projectId }),
|
|
}
|
|
);
|
|
|
|
const data = await response.json();
|
|
|
|
// Retourne les modèles avec les informations de quota
|
|
return Object.entries(data.models).map(([modelId, modelInfo]) => ({
|
|
id: modelId,
|
|
displayName: modelInfo.displayName,
|
|
quotaInfo: {
|
|
remainingFraction: modelInfo.quotaInfo?.remainingFraction,
|
|
resetTime: modelInfo.quotaInfo?.resetTime,
|
|
isExhausted: modelInfo.quotaInfo?.isExhausted,
|
|
},
|
|
}));
|
|
}
|
|
```
|
|
|
|
### Format de réponse
|
|
|
|
```typescript
|
|
type FetchAvailableModelsResponse = {
|
|
models?: Record<string, {
|
|
displayName?: string;
|
|
quotaInfo?: {
|
|
remainingFraction?: number | string;
|
|
resetTime?: string; // Horodatage ISO 8601
|
|
isExhausted?: boolean;
|
|
};
|
|
}>;
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## Suivi de l'utilisation
|
|
|
|
### Récupérer les données d'utilisation
|
|
|
|
```typescript
|
|
export async function fetchAntigravityUsage(
|
|
token: string,
|
|
timeoutMs: number
|
|
): Promise<ProviderUsageSnapshot> {
|
|
// 1. Récupérer les crédits et les informations du plan
|
|
const loadCodeAssistRes = await fetch(
|
|
`${BASE_URL}/v1internal:loadCodeAssist`,
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
Authorization: `Bearer ${token}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
metadata: {
|
|
ideType: "ANTIGRAVITY",
|
|
platform: "PLATFORM_UNSPECIFIED",
|
|
pluginType: "GEMINI",
|
|
},
|
|
}),
|
|
}
|
|
);
|
|
|
|
// Extraire les informations de crédits
|
|
const { availablePromptCredits, planInfo, currentTier } = data;
|
|
|
|
// 2. Récupérer les quotas des modèles
|
|
const modelsRes = await fetch(
|
|
`${BASE_URL}/v1internal:fetchAvailableModels`,
|
|
{
|
|
method: "POST",
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
body: JSON.stringify({ project: projectId }),
|
|
}
|
|
);
|
|
|
|
// Construire les fenêtres d'utilisation
|
|
return {
|
|
provider: "google-antigravity",
|
|
displayName: "Google Antigravity",
|
|
windows: [
|
|
{ label: "Credits", usedPercent: calculateUsedPercent(available, monthly) },
|
|
// Quotas individuels des modèles...
|
|
],
|
|
plan: currentTier?.name || planType,
|
|
};
|
|
}
|
|
```
|
|
|
|
### Structure de la réponse d'utilisation
|
|
|
|
```typescript
|
|
type ProviderUsageSnapshot = {
|
|
provider: "google-antigravity";
|
|
displayName: string;
|
|
windows: UsageWindow[];
|
|
plan?: string;
|
|
error?: string;
|
|
};
|
|
|
|
type UsageWindow = {
|
|
label: string; // "Credits" ou ID du modèle
|
|
usedPercent: number; // 0-100
|
|
resetAt?: number; // Horodatage de réinitialisation du quota
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## Structure du plugin fournisseur
|
|
|
|
### Définition du plugin
|
|
|
|
```typescript
|
|
const antigravityPlugin = {
|
|
id: "google-antigravity-auth",
|
|
name: "Google Antigravity Auth",
|
|
description: "OAuth flow for Google Antigravity (Cloud Code Assist)",
|
|
configSchema: emptyPluginConfigSchema(),
|
|
|
|
register(api: PicoClawPluginApi) {
|
|
api.registerProvider({
|
|
id: "google-antigravity",
|
|
label: "Google Antigravity",
|
|
docsPath: "/providers/models",
|
|
aliases: ["antigravity"],
|
|
|
|
auth: [
|
|
{
|
|
id: "oauth",
|
|
label: "Google OAuth",
|
|
hint: "PKCE + localhost callback",
|
|
kind: "oauth",
|
|
run: async (ctx: ProviderAuthContext) => {
|
|
// Implémentation OAuth ici
|
|
},
|
|
},
|
|
],
|
|
});
|
|
},
|
|
};
|
|
```
|
|
|
|
### ProviderAuthContext
|
|
|
|
```typescript
|
|
type ProviderAuthContext = {
|
|
config: PicoClawConfig;
|
|
agentDir?: string;
|
|
workspaceDir?: string;
|
|
prompter: WizardPrompter; // Invites/notifications UI
|
|
runtime: RuntimeEnv; // Journalisation, etc.
|
|
isRemote: boolean; // Exécution à distance ou non
|
|
openUrl: (url: string) => Promise<void>; // Ouverture du navigateur
|
|
oauth: {
|
|
createVpsAwareHandlers: Function;
|
|
};
|
|
};
|
|
```
|
|
|
|
### ProviderAuthResult
|
|
|
|
```typescript
|
|
type ProviderAuthResult = {
|
|
profiles: Array<{
|
|
profileId: string;
|
|
credential: AuthProfileCredential;
|
|
}>;
|
|
configPatch?: Partial<PicoClawConfig>;
|
|
defaultModel?: string;
|
|
notes?: string[];
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## Exigences d'intégration
|
|
|
|
### 1. Environnement/dépendances requis
|
|
|
|
- Go ≥ 1.25
|
|
- Base de code PicoClaw (`pkg/providers/` et `pkg/auth/`)
|
|
- Packages de la bibliothèque standard `crypto` et `net/http`
|
|
|
|
### 2. En-têtes requis pour les appels API
|
|
|
|
```typescript
|
|
const REQUIRED_HEADERS = {
|
|
"Authorization": `Bearer ${accessToken}`,
|
|
"Content-Type": "application/json",
|
|
"User-Agent": "antigravity", // ou "google-api-nodejs-client/9.15.1"
|
|
"X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
|
|
};
|
|
|
|
// Pour les appels loadCodeAssist, inclure également :
|
|
const CLIENT_METADATA = {
|
|
ideType: "ANTIGRAVITY", // ou "IDE_UNSPECIFIED"
|
|
platform: "PLATFORM_UNSPECIFIED",
|
|
pluginType: "GEMINI",
|
|
};
|
|
```
|
|
|
|
### 3. Assainissement des schémas de modèles
|
|
|
|
Antigravity utilise des modèles compatibles Gemini, les schémas d'outils doivent donc être assainis :
|
|
|
|
```typescript
|
|
const GOOGLE_SCHEMA_UNSUPPORTED_KEYWORDS = new Set([
|
|
"patternProperties",
|
|
"additionalProperties",
|
|
"$schema",
|
|
"$id",
|
|
"$ref",
|
|
"$defs",
|
|
"definitions",
|
|
"examples",
|
|
"minLength",
|
|
"maxLength",
|
|
"minimum",
|
|
"maximum",
|
|
"multipleOf",
|
|
"pattern",
|
|
"format",
|
|
"minItems",
|
|
"maxItems",
|
|
"uniqueItems",
|
|
"minProperties",
|
|
"maxProperties",
|
|
]);
|
|
|
|
// Nettoyer le schéma avant l'envoi
|
|
function cleanToolSchemaForGemini(schema: Record<string, unknown>): unknown {
|
|
// Supprimer les mots-clés non supportés
|
|
// S'assurer que le niveau supérieur a type: "object"
|
|
// Aplatir les unions anyOf/oneOf
|
|
}
|
|
```
|
|
|
|
### 4. Gestion des blocs de réflexion (modèles Claude)
|
|
|
|
Pour les modèles Claude via Antigravity, les blocs de réflexion nécessitent un traitement spécial :
|
|
|
|
```typescript
|
|
const ANTIGRAVITY_SIGNATURE_RE = /^[A-Za-z0-9+/]+={0,2}$/;
|
|
|
|
export function sanitizeAntigravityThinkingBlocks(
|
|
messages: AgentMessage[]
|
|
): AgentMessage[] {
|
|
// Valider les signatures de réflexion
|
|
// Normaliser les champs de signature
|
|
// Rejeter les blocs de réflexion non signés
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Points de terminaison API
|
|
|
|
### Points de terminaison d'authentification
|
|
|
|
| Point de terminaison | Méthode | Objectif |
|
|
|---------------------|---------|----------|
|
|
| `https://accounts.google.com/o/oauth2/v2/auth` | GET | Autorisation OAuth |
|
|
| `https://oauth2.googleapis.com/token` | POST | Échange de jetons |
|
|
| `https://www.googleapis.com/oauth2/v1/userinfo` | GET | Informations utilisateur (e-mail) |
|
|
|
|
### Points de terminaison Cloud Code Assist
|
|
|
|
| Point de terminaison | Méthode | Objectif |
|
|
|---------------------|---------|----------|
|
|
| `https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist` | POST | Charger les infos du projet, crédits, plan |
|
|
| `https://cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels` | POST | Lister les modèles disponibles avec quotas |
|
|
| `https://cloudcode-pa.googleapis.com/v1internal:streamGenerateContent?alt=sse` | POST | Point de terminaison de streaming de chat |
|
|
|
|
**Format de requête API (chat) :**
|
|
Le point de terminaison `v1internal:streamGenerateContent` attend une enveloppe encapsulant la requête Gemini standard :
|
|
|
|
```json
|
|
{
|
|
"project": "your-project-id",
|
|
"model": "model-id",
|
|
"request": {
|
|
"contents": [...],
|
|
"systemInstruction": {...},
|
|
"generationConfig": {...},
|
|
"tools": [...]
|
|
},
|
|
"requestType": "agent",
|
|
"userAgent": "antigravity",
|
|
"requestId": "agent-timestamp-random"
|
|
}
|
|
```
|
|
|
|
**Format de réponse API (SSE) :**
|
|
Chaque message SSE (`data: {...}`) est encapsulé dans un champ `response` :
|
|
|
|
```json
|
|
{
|
|
"response": {
|
|
"candidates": [...],
|
|
"usageMetadata": {...},
|
|
"modelVersion": "...",
|
|
"responseId": "..."
|
|
},
|
|
"traceId": "...",
|
|
"metadata": {}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Configuration
|
|
|
|
### Configuration config.json
|
|
|
|
```json
|
|
{
|
|
"model_list": [
|
|
{
|
|
"model_name": "gemini-flash",
|
|
"model": "antigravity/gemini-3-flash",
|
|
"auth_method": "oauth"
|
|
}
|
|
],
|
|
"agents": {
|
|
"defaults": {
|
|
"model_name": "gemini-flash"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Stockage du profil d'authentification
|
|
|
|
Les profils d'authentification sont stockés dans `~/.picoclaw/auth.json` :
|
|
|
|
```json
|
|
{
|
|
"credentials": {
|
|
"google-antigravity": {
|
|
"access_token": "ya29...",
|
|
"refresh_token": "1//...",
|
|
"expires_at": "2026-01-01T00:00:00Z",
|
|
"provider": "google-antigravity",
|
|
"auth_method": "oauth",
|
|
"email": "user@example.com",
|
|
"project_id": "my-project-id"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Créer un nouveau fournisseur dans PicoClaw
|
|
|
|
Les fournisseurs PicoClaw sont implémentés en tant que packages Go sous `pkg/providers/`. Pour ajouter un nouveau fournisseur :
|
|
|
|
### Implémentation étape par étape
|
|
|
|
#### 1. Créer le fichier du fournisseur
|
|
|
|
Créez un nouveau fichier Go dans `pkg/providers/` :
|
|
|
|
```
|
|
pkg/providers/
|
|
└── your_provider.go
|
|
```
|
|
|
|
#### 2. Implémenter l'interface Provider
|
|
|
|
Votre fournisseur doit implémenter l'interface `Provider` définie dans `pkg/providers/types.go` :
|
|
|
|
```go
|
|
package providers
|
|
|
|
type YourProvider struct {
|
|
apiKey string
|
|
apiBase string
|
|
}
|
|
|
|
func NewYourProvider(apiKey, apiBase, proxy string) *YourProvider {
|
|
if apiBase == "" {
|
|
apiBase = "https://api.your-provider.com/v1"
|
|
}
|
|
return &YourProvider{apiKey: apiKey, apiBase: apiBase}
|
|
}
|
|
|
|
func (p *YourProvider) Chat(ctx context.Context, messages []Message, tools []Tool, cb StreamCallback) error {
|
|
// Implémenter la complétion de chat avec streaming
|
|
}
|
|
```
|
|
|
|
#### 3. Enregistrer dans la factory
|
|
|
|
Ajoutez votre fournisseur au switch de protocole dans `pkg/providers/factory.go` :
|
|
|
|
```go
|
|
case "your-provider":
|
|
return NewYourProvider(sel.apiKey, sel.apiBase, sel.proxy), nil
|
|
```
|
|
|
|
#### 4. Ajouter la configuration par défaut (optionnel)
|
|
|
|
Ajoutez une entrée par défaut dans `pkg/config/defaults.go` :
|
|
|
|
```go
|
|
{
|
|
ModelName: "your-model",
|
|
Model: "your-provider/model-name",
|
|
APIKey: "",
|
|
},
|
|
```
|
|
|
|
#### 5. Ajouter le support d'authentification (optionnel)
|
|
|
|
Si votre fournisseur nécessite OAuth ou une authentification spéciale, ajoutez un cas dans `cmd/picoclaw/internal/auth/helpers.go` :
|
|
|
|
```go
|
|
case "your-provider":
|
|
authLoginYourProvider()
|
|
```
|
|
|
|
#### 6. Configurer via `config.json`
|
|
|
|
```json
|
|
{
|
|
"model_list": [
|
|
{
|
|
"model_name": "your-model",
|
|
"model": "your-provider/model-name",
|
|
"api_keys": ["your-api-key"],
|
|
"api_base": "https://api.your-provider.com/v1"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Tester votre implémentation
|
|
|
|
### Commandes CLI
|
|
|
|
```bash
|
|
# S'authentifier avec un fournisseur
|
|
picoclaw auth login --provider your-provider
|
|
|
|
# Lister les modèles (pour Antigravity)
|
|
picoclaw auth models
|
|
|
|
# Démarrer la passerelle
|
|
picoclaw gateway
|
|
|
|
# Exécuter un agent avec un modèle spécifique
|
|
picoclaw agent -m "Hello" --model your-model
|
|
```
|
|
|
|
### Variables d'environnement pour les tests
|
|
|
|
```bash
|
|
# Remplacer le modèle par défaut
|
|
export PICOCLAW_AGENTS_DEFAULTS_MODEL=your-model
|
|
|
|
# Remplacer les paramètres du fournisseur
|
|
export PICOCLAW_MODEL_LIST='[{"model_name":"your-model","model":"your-provider/model-name","api_keys":["..."]}]'
|
|
```
|
|
|
|
---
|
|
|
|
## Références
|
|
|
|
- **Fichiers source :**
|
|
- `pkg/providers/antigravity_provider.go` - Implémentation du fournisseur Antigravity
|
|
- `pkg/auth/oauth.go` - Implémentation du flux OAuth
|
|
- `pkg/auth/store.go` - Stockage des identifiants d'authentification (`~/.picoclaw/auth.json`)
|
|
- `pkg/providers/factory.go` - Factory des fournisseurs et routage de protocole
|
|
- `pkg/providers/types.go` - Définitions de l'interface fournisseur
|
|
- `cmd/picoclaw/internal/auth/helpers.go` - Commandes CLI d'authentification
|
|
|
|
- **Documentation :**
|
|
- `docs/ANTIGRAVITY_USAGE.md` - Guide d'utilisation d'Antigravity
|
|
- `docs/migration/model-list-migration.md` - Guide de migration
|
|
|
|
---
|
|
|
|
## Notes
|
|
|
|
1. **Projet Google Cloud :** Antigravity nécessite que Gemini for Google Cloud soit activé sur votre projet Google Cloud
|
|
2. **Quotas :** Utilise les quotas du projet Google Cloud (pas de facturation séparée)
|
|
3. **Accès aux modèles :** Les modèles disponibles dépendent de la configuration de votre projet Google Cloud
|
|
4. **Blocs de réflexion :** Les modèles Claude via Antigravity nécessitent un traitement spécial des blocs de réflexion avec signatures
|
|
5. **Assainissement des schémas :** Les schémas d'outils doivent être assainis pour supprimer les mots-clés JSON Schema non supportés
|
|
|
|
---
|
|
|
|
---
|
|
|
|
## Gestion des erreurs courantes
|
|
|
|
### 1. Limitation de débit (HTTP 429)
|
|
|
|
Antigravity retourne une erreur 429 lorsque les quotas du projet/modèle sont épuisés. La réponse d'erreur contient souvent un `quotaResetDelay` dans le champ `details`.
|
|
|
|
**Exemple d'erreur 429 :**
|
|
```json
|
|
{
|
|
"error": {
|
|
"code": 429,
|
|
"message": "You have exhausted your capacity on this model. Your quota will reset after 4h30m28s.",
|
|
"status": "RESOURCE_EXHAUSTED",
|
|
"details": [
|
|
{
|
|
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
|
|
"metadata": {
|
|
"quotaResetDelay": "4h30m28.060903746s"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2. Réponses vides (modèles restreints)
|
|
|
|
Certains modèles peuvent apparaître dans la liste des modèles disponibles mais retourner une réponse vide (200 OK mais flux SSE vide). Cela se produit généralement pour les modèles en préversion ou restreints que le projet actuel n'a pas la permission d'utiliser.
|
|
|
|
**Traitement :** Traiter les réponses vides comme des erreurs informant l'utilisateur que le modèle pourrait être restreint ou invalide pour son projet.
|
|
|
|
---
|
|
|
|
## Dépannage
|
|
|
|
### "Token expired" (jeton expiré)
|
|
- Rafraîchir les jetons OAuth : `picoclaw auth login --provider antigravity`
|
|
|
|
### "Gemini for Google Cloud is not enabled" (Gemini for Google Cloud n'est pas activé)
|
|
- Activer l'API dans votre Google Cloud Console
|
|
|
|
### "Project not found" (projet non trouvé)
|
|
- Vérifier que votre projet Google Cloud a les API nécessaires activées
|
|
- Vérifier que l'ID du projet est correctement récupéré lors de l'authentification
|
|
|
|
### Les modèles n'apparaissent pas dans la liste
|
|
- Vérifier que l'authentification OAuth s'est terminée avec succès
|
|
- Vérifier le stockage du profil d'authentification : `~/.picoclaw/auth.json`
|
|
- Relancer `picoclaw auth login --provider antigravity`
|