| 
 | 1 | +import type { CancellationToken } from 'vscode';  | 
 | 2 | +import type { Response } from '@env/fetch';  | 
 | 3 | +import { mistralProviderDescriptor as provider } from '../../constants.ai';  | 
 | 4 | +import { AIError, AIErrorReason } from '../../errors';  | 
 | 5 | +import type { AIActionType, AIModel } from './models/model';  | 
 | 6 | +import { OpenAICompatibleProviderBase } from './openAICompatibleProviderBase';  | 
 | 7 | + | 
 | 8 | +type MistralModel = AIModel<typeof provider.id>;  | 
 | 9 | +const models: MistralModel[] = [  | 
 | 10 | +	{  | 
 | 11 | +		id: 'mistral-medium-latest',  | 
 | 12 | +		name: 'Mistral Medium',  | 
 | 13 | +		maxTokens: { input: 131072, output: 4096 },  | 
 | 14 | +		provider: provider,  | 
 | 15 | +	},  | 
 | 16 | +	{  | 
 | 17 | +		id: 'mistral-medium-2505',  | 
 | 18 | +		name: 'Mistral Medium',  | 
 | 19 | +		maxTokens: { input: 131072, output: 4096 },  | 
 | 20 | +		provider: provider,  | 
 | 21 | +		hidden: true,  | 
 | 22 | +	},  | 
 | 23 | +	{  | 
 | 24 | +		id: 'codestral-latest',  | 
 | 25 | +		name: 'Codestral',  | 
 | 26 | +		maxTokens: { input: 262144, output: 4096 },  | 
 | 27 | +		provider: provider,  | 
 | 28 | +		default: true,  | 
 | 29 | +	},  | 
 | 30 | +	{  | 
 | 31 | +		id: 'codestral-2501',  | 
 | 32 | +		name: 'Codestral',  | 
 | 33 | +		maxTokens: { input: 262144, output: 4096 },  | 
 | 34 | +		provider: provider,  | 
 | 35 | +		hidden: true,  | 
 | 36 | +	},  | 
 | 37 | +	{  | 
 | 38 | +		id: 'mistral-large-latest',  | 
 | 39 | +		name: 'Mistral Large',  | 
 | 40 | +		maxTokens: { input: 131072, output: 4096 },  | 
 | 41 | +		provider: provider,  | 
 | 42 | +	},  | 
 | 43 | +	{  | 
 | 44 | +		id: 'mistral-large-2411',  | 
 | 45 | +		name: 'Mistral Large',  | 
 | 46 | +		maxTokens: { input: 131072, output: 4096 },  | 
 | 47 | +		provider: provider,  | 
 | 48 | +		hidden: true,  | 
 | 49 | +	},  | 
 | 50 | +	{  | 
 | 51 | +		id: 'devstral-small-latest',  | 
 | 52 | +		name: 'Devstral Small',  | 
 | 53 | +		maxTokens: { input: 131072, output: 4096 },  | 
 | 54 | +		provider: provider,  | 
 | 55 | +	},  | 
 | 56 | +	{  | 
 | 57 | +		id: 'devstral-small-2505',  | 
 | 58 | +		name: 'Devstral Small',  | 
 | 59 | +		maxTokens: { input: 131072, output: 4096 },  | 
 | 60 | +		provider: provider,  | 
 | 61 | +		hidden: true,  | 
 | 62 | +	},  | 
 | 63 | +	{  | 
 | 64 | +		id: 'mistral-small-latest',  | 
 | 65 | +		name: 'Mistral Small',  | 
 | 66 | +		maxTokens: { input: 131072, output: 4096 },  | 
 | 67 | +		provider: provider,  | 
 | 68 | +	},  | 
 | 69 | +	{  | 
 | 70 | +		id: 'mistral-small-2503',  | 
 | 71 | +		name: 'Mistral Small',  | 
 | 72 | +		maxTokens: { input: 131072, output: 4096 },  | 
 | 73 | +		provider: provider,  | 
 | 74 | +		hidden: true,  | 
 | 75 | +	},  | 
 | 76 | +];  | 
 | 77 | + | 
 | 78 | +export class MistralProvider extends OpenAICompatibleProviderBase<typeof provider.id> {  | 
 | 79 | +	readonly id = provider.id;  | 
 | 80 | +	readonly name = provider.name;  | 
 | 81 | +	protected readonly descriptor = provider;  | 
 | 82 | +	protected readonly config = {  | 
 | 83 | +		keyUrl: 'https://console.mistral.ai/api-keys',  | 
 | 84 | +		keyValidator: /^[a-zA-Z0-9]{32}$/,  | 
 | 85 | +	};  | 
 | 86 | + | 
 | 87 | +	getModels(): Promise<readonly AIModel<typeof provider.id>[]> {  | 
 | 88 | +		return Promise.resolve(models);  | 
 | 89 | +	}  | 
 | 90 | + | 
 | 91 | +	protected getUrl(_model: AIModel<typeof provider.id>): string {  | 
 | 92 | +		return 'https://api.mistral.ai/v1/chat/completions';  | 
 | 93 | +	}  | 
 | 94 | + | 
 | 95 | +	protected override fetchCore<TAction extends AIActionType>(  | 
 | 96 | +		action: TAction,  | 
 | 97 | +		model: AIModel<typeof provider.id>,  | 
 | 98 | +		apiKey: string,  | 
 | 99 | +		request: object,  | 
 | 100 | +		cancellation: CancellationToken | undefined,  | 
 | 101 | +	): Promise<Response> {  | 
 | 102 | +		if ('max_completion_tokens' in request) {  | 
 | 103 | +			const { max_completion_tokens: max, ...rest } = request;  | 
 | 104 | +			request = max ? { max_tokens: max, ...rest } : rest;  | 
 | 105 | +		}  | 
 | 106 | +		return super.fetchCore(action, model, apiKey, request, cancellation);  | 
 | 107 | +	}  | 
 | 108 | + | 
 | 109 | +	protected override async handleFetchFailure<TAction extends AIActionType>(  | 
 | 110 | +		rsp: Response,  | 
 | 111 | +		action: TAction,  | 
 | 112 | +		model: AIModel<typeof provider.id>,  | 
 | 113 | +		retries: number,  | 
 | 114 | +		maxInputTokens: number,  | 
 | 115 | +	): Promise<{ retry: true; maxInputTokens: number }> {  | 
 | 116 | +		if (rsp.status !== 404 && rsp.status !== 429) {  | 
 | 117 | +			let json;  | 
 | 118 | +			try {  | 
 | 119 | +				json = (await rsp.json()) as MistralError | undefined;  | 
 | 120 | +			} catch {}  | 
 | 121 | + | 
 | 122 | +			debugger;  | 
 | 123 | + | 
 | 124 | +			const message = `${json?.type}: ${json?.message?.detail  | 
 | 125 | +				?.map(d => `${d.msg} (${d.type} @ ${d.loc.join(',')})`)  | 
 | 126 | +				.join(', ')}`;  | 
 | 127 | + | 
 | 128 | +			if (json?.type === 'invalid_request_error') {  | 
 | 129 | +				if (message?.includes('prompt is too long')) {  | 
 | 130 | +					if (retries < 2) {  | 
 | 131 | +						return { retry: true, maxInputTokens: maxInputTokens - 200 * (retries || 1) };  | 
 | 132 | +					}  | 
 | 133 | + | 
 | 134 | +					throw new AIError(  | 
 | 135 | +						AIErrorReason.RequestTooLarge,  | 
 | 136 | +						new Error(`(${this.name}) ${rsp.status}: ${message || rsp.statusText}`),  | 
 | 137 | +					);  | 
 | 138 | +				}  | 
 | 139 | +			}  | 
 | 140 | + | 
 | 141 | +			throw new Error(`(${this.name}) ${rsp.status}: ${message || rsp.statusText}`);  | 
 | 142 | +		}  | 
 | 143 | + | 
 | 144 | +		return super.handleFetchFailure(rsp, action, model, retries, maxInputTokens);  | 
 | 145 | +	}  | 
 | 146 | +}  | 
 | 147 | + | 
 | 148 | +interface MistralError {  | 
 | 149 | +	object: 'error';  | 
 | 150 | +	type: 'invalid_request_error' | string;  | 
 | 151 | +	message: {  | 
 | 152 | +		detail: {  | 
 | 153 | +			type: string;  | 
 | 154 | +			msg: string;  | 
 | 155 | +			loc: string[];  | 
 | 156 | +			input: number;  | 
 | 157 | +		}[];  | 
 | 158 | +	};  | 
 | 159 | +	code: unknown | null;  | 
 | 160 | +	param: unknown | null;  | 
 | 161 | +}  | 
0 commit comments