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
63 changes: 40 additions & 23 deletions lib/data/provider/ai/ask_ai.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class AskAiRepository {
throw AskAiConfigException(invalidBaseUrl: baseUrl);
}

final uri = _composeUri(baseUrl, '/v1/chat/completions');
final uri = composeChatCompletionsUri(baseUrl);
final authHeader = apiKey.startsWith('Bearer ') ? apiKey : 'Bearer $apiKey';
final headers = <String, String>{
Headers.acceptHeader: 'text/event-stream',
Expand Down Expand Up @@ -72,7 +72,10 @@ class AskAiRepository {
),
);
} on DioException catch (e) {
throw AskAiNetworkException(message: e.message ?? 'Request failed', cause: e);
throw AskAiNetworkException(
message: e.message ?? 'Request failed',
cause: e,
);
}

final body = response.data;
Expand Down Expand Up @@ -153,7 +156,10 @@ class AskAiRepository {
for (final toolCall in toolCalls) {
if (toolCall is! Map<String, dynamic>) continue;
final index = toolCall['index'] as int? ?? 0;
final builder = toolBuilders.putIfAbsent(index, _ToolCallBuilder.new);
final builder = toolBuilders.putIfAbsent(
index,
_ToolCallBuilder.new,
);
final function = toolCall['function'];
if (function is Map<String, dynamic>) {
builder.name ??= function['name'] as String?;
Expand Down Expand Up @@ -213,23 +219,15 @@ class AskAiRepository {
..writeln('仅在非常确定命令安全时才给出建议。');

if (localeHint != null && localeHint.isNotEmpty) {
promptBuffer
.writeln('请优先使用用户的语言输出:$localeHint。');
promptBuffer.writeln('请优先使用用户的语言输出:$localeHint。');
}

final messages = <Map<String, String>>[
{
'role': 'system',
'content': promptBuffer.toString(),
},
...conversation.map((message) => {
'role': message.apiRole,
'content': message.content,
}),
{
'role': 'user',
'content': '以下是终端选中的内容:\n$selection',
},
{'role': 'system', 'content': promptBuffer.toString()},
...conversation.map(
(message) => {'role': message.apiRole, 'content': message.content},
),
{'role': 'user', 'content': '以下是终端选中的内容:\n$selection'},
];

return {
Expand Down Expand Up @@ -262,10 +260,23 @@ class AskAiRepository {
};
}

Uri _composeUri(String base, String path) {
final sanitizedBase = base.replaceAll(RegExp(r'/+$'), '');
final sanitizedPath = path.replaceFirst(RegExp(r'^/+'), '');
return Uri.parse('$sanitizedBase/$sanitizedPath');
@visibleForTesting
static Uri composeChatCompletionsUri(String endpoint) {
final uri = Uri.parse(endpoint.replaceAll(RegExp(r'/+$'), ''));
final segments = uri.pathSegments;
final hasChatCompletionsPath =
segments.length >= 2 &&
segments[segments.length - 2] == 'chat' &&
segments.last == 'completions';

if (hasChatCompletionsPath) {
return uri;
}

final appendSegments = segments.isNotEmpty && segments.last == 'v1'
? ['chat', 'completions']
: ['v1', 'chat', 'completions'];
return uri.replace(pathSegments: [...segments, ...appendSegments]);
}
}

Expand All @@ -288,7 +299,10 @@ class _ToolCallBuilder {
}
return null;
}
final description = decoded['description'] as String? ?? decoded['explanation'] as String? ?? '';
final description =
decoded['description'] as String? ??
decoded['explanation'] as String? ??
'';
_emitted = true;
return AskAiCommand(
command: command.trim(),
Expand All @@ -308,7 +322,10 @@ class _ToolCallBuilder {
enum AskAiConfigField { baseUrl, apiKey, model }

class AskAiConfigException implements Exception {
const AskAiConfigException({this.missingFields = const [], this.invalidBaseUrl});
const AskAiConfigException({
this.missingFields = const [],
this.invalidBaseUrl,
});

final List<AskAiConfigField> missingFields;
final String? invalidBaseUrl;
Expand Down
2 changes: 1 addition & 1 deletion lib/data/store/setting.dart
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ class SettingStore extends HiveStore {
/// Terminal AI helper configuration
late final askAiBaseUrl = propertyDefault('askAiBaseUrl', 'https://api.openai.com');
late final askAiApiKey = propertyDefault('askAiApiKey', '');
late final askAiModel = propertyDefault('askAiModel', 'gpt-4o-mini');
late final askAiModel = propertyDefault('askAiModel', 'gpt-5.4-mini');

late final serverFuncBtns = listProperty('serverBtns', defaultValue: ServerFuncBtn.defaultIdxs);

Expand Down
8 changes: 7 additions & 1 deletion lib/generated/l10n/l10n.dart
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,15 @@ abstract class AppLocalizations {
/// No description provided for @askAiBaseUrl.
///
/// In en, this message translates to:
/// **'Base URL'**
/// **'API Endpoint'**
String get askAiBaseUrl;

/// No description provided for @askAiEndpointTip.
///
/// In en, this message translates to:
/// **'Enter the full Chat Completions endpoint, or a service base URL. If the address ends with /v1, the app will append /chat/completions.'**
String get askAiEndpointTip;

/// No description provided for @askAiCommandInserted.
///
/// In en, this message translates to:
Expand Down
4 changes: 4 additions & 0 deletions lib/generated/l10n/l10n_de.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get askAiBaseUrl => 'Basis-URL';

@override
String get askAiEndpointTip =>
'Enter the full Chat Completions endpoint, or a service base URL. If the address ends with /v1, the app will append /chat/completions.';

@override
String get askAiCommandInserted => 'Befehl ins Terminal eingefügt';

Expand Down
6 changes: 5 additions & 1 deletion lib/generated/l10n/l10n_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ class AppLocalizationsEn extends AppLocalizations {
String get askAiAwaitingResponse => 'Waiting for AI response...';

@override
String get askAiBaseUrl => 'Base URL';
String get askAiBaseUrl => 'API Endpoint';

@override
String get askAiEndpointTip =>
'Enter the full Chat Completions endpoint, or a service base URL. If the address ends with /v1, the app will append /chat/completions.';

@override
String get askAiCommandInserted => 'Command inserted into terminal';
Expand Down
4 changes: 4 additions & 0 deletions lib/generated/l10n/l10n_es.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get askAiBaseUrl => 'URL base';

@override
String get askAiEndpointTip =>
'Enter the full Chat Completions endpoint, or a service base URL. If the address ends with /v1, the app will append /chat/completions.';

@override
String get askAiCommandInserted => 'Comando insertado en el terminal';

Expand Down
4 changes: 4 additions & 0 deletions lib/generated/l10n/l10n_fr.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get askAiBaseUrl => 'URL de base';

@override
String get askAiEndpointTip =>
'Enter the full Chat Completions endpoint, or a service base URL. If the address ends with /v1, the app will append /chat/completions.';

@override
String get askAiCommandInserted => 'Commande insérée dans le terminal';

Expand Down
4 changes: 4 additions & 0 deletions lib/generated/l10n/l10n_id.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get askAiBaseUrl => 'URL dasar';

@override
String get askAiEndpointTip =>
'Enter the full Chat Completions endpoint, or a service base URL. If the address ends with /v1, the app will append /chat/completions.';

@override
String get askAiCommandInserted => 'Perintah dimasukkan ke terminal';

Expand Down
4 changes: 4 additions & 0 deletions lib/generated/l10n/l10n_it.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get askAiBaseUrl => 'URL base';

@override
String get askAiEndpointTip =>
'Enter the full Chat Completions endpoint, or a service base URL. If the address ends with /v1, the app will append /chat/completions.';

@override
String get askAiCommandInserted => 'Comando inserito nel terminale';

Expand Down
4 changes: 4 additions & 0 deletions lib/generated/l10n/l10n_ja.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ class AppLocalizationsJa extends AppLocalizations {
@override
String get askAiBaseUrl => 'ベース URL';

@override
String get askAiEndpointTip =>
'Enter the full Chat Completions endpoint, or a service base URL. If the address ends with /v1, the app will append /chat/completions.';

@override
String get askAiCommandInserted => 'コマンドをターミナルに挿入しました';

Expand Down
4 changes: 4 additions & 0 deletions lib/generated/l10n/l10n_ko.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ class AppLocalizationsKo extends AppLocalizations {
@override
String get askAiBaseUrl => '기본 URL';

@override
String get askAiEndpointTip =>
'Enter the full Chat Completions endpoint, or a service base URL. If the address ends with /v1, the app will append /chat/completions.';

@override
String get askAiCommandInserted => '명령어가 터미널에 삽입되었습니다';

Expand Down
4 changes: 4 additions & 0 deletions lib/generated/l10n/l10n_nl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get askAiBaseUrl => 'Basis-URL';

@override
String get askAiEndpointTip =>
'Enter the full Chat Completions endpoint, or a service base URL. If the address ends with /v1, the app will append /chat/completions.';

@override
String get askAiCommandInserted => 'Commando in terminal ingevoegd';

Expand Down
4 changes: 4 additions & 0 deletions lib/generated/l10n/l10n_pt.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get askAiBaseUrl => 'URL base';

@override
String get askAiEndpointTip =>
'Enter the full Chat Completions endpoint, or a service base URL. If the address ends with /v1, the app will append /chat/completions.';

@override
String get askAiCommandInserted => 'Comando inserido no terminal';

Expand Down
4 changes: 4 additions & 0 deletions lib/generated/l10n/l10n_ru.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get askAiBaseUrl => 'Базовый URL';

@override
String get askAiEndpointTip =>
'Enter the full Chat Completions endpoint, or a service base URL. If the address ends with /v1, the app will append /chat/completions.';

@override
String get askAiCommandInserted => 'Команда вставлена в терминал';

Expand Down
4 changes: 4 additions & 0 deletions lib/generated/l10n/l10n_tr.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ class AppLocalizationsTr extends AppLocalizations {
@override
String get askAiBaseUrl => 'Temel URL';

@override
String get askAiEndpointTip =>
'Enter the full Chat Completions endpoint, or a service base URL. If the address ends with /v1, the app will append /chat/completions.';

@override
String get askAiCommandInserted => 'Komut terminale eklendi';

Expand Down
4 changes: 4 additions & 0 deletions lib/generated/l10n/l10n_uk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ class AppLocalizationsUk extends AppLocalizations {
@override
String get askAiBaseUrl => 'Базова URL';

@override
String get askAiEndpointTip =>
'Enter the full Chat Completions endpoint, or a service base URL. If the address ends with /v1, the app will append /chat/completions.';

@override
String get askAiCommandInserted => 'Команду вставлено в термінал';

Expand Down
12 changes: 10 additions & 2 deletions lib/generated/l10n/l10n_zh.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ class AppLocalizationsZh extends AppLocalizations {
String get askAiAwaitingResponse => '等待 AI 响应...';

@override
String get askAiBaseUrl => '基础 URL';
String get askAiBaseUrl => 'API 接口地址';

@override
String get askAiEndpointTip =>
'填写完整的 Chat Completions 地址,或填写服务根地址。若地址以 /v1 结尾,应用会自动补全 /chat/completions。';

@override
String get askAiCommandInserted => '命令已插入终端';
Expand Down Expand Up @@ -1047,7 +1051,11 @@ class AppLocalizationsZhTw extends AppLocalizationsZh {
String get askAiAwaitingResponse => '等待 AI 回應...';

@override
String get askAiBaseUrl => '基礎 URL';
String get askAiBaseUrl => 'API 介面位址';

@override
String get askAiEndpointTip =>
'填寫完整的 Chat Completions 位址,或填寫服務根位址。若位址以 /v1 結尾,應用程式會自動補上 /chat/completions。';

@override
String get askAiCommandInserted => '指令已插入終端機';
Expand Down
3 changes: 2 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"ai": "AI",
"askAiApiKey": "API Key",
"askAiAwaitingResponse": "Waiting for AI response...",
"askAiBaseUrl": "Base URL",
"askAiBaseUrl": "API Endpoint",
"askAiEndpointTip": "Enter the full Chat Completions endpoint, or a service base URL. If the address ends with /v1, the app will append /chat/completions.",
"askAiCommandInserted": "Command inserted into terminal",
"askAiConfigMissing": "Please configure {fields} in Settings.",
"askAiConfirmExecute": "Confirm before executing",
Expand Down
3 changes: 2 additions & 1 deletion lib/l10n/app_zh.arb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"askAi": "问 AI",
"askAiApiKey": "API 密钥",
"askAiAwaitingResponse": "等待 AI 响应...",
"askAiBaseUrl": "基础 URL",
"askAiBaseUrl": "API 接口地址",
"askAiEndpointTip": "填写完整的 Chat Completions 地址,或填写服务根地址。若地址以 /v1 结尾,应用会自动补全 /chat/completions。",
"askAiCommandInserted": "命令已插入终端",
"askAiConfigMissing": "请前往设置配置 {fields}",
"askAiConfirmExecute": "执行前确认",
Expand Down
3 changes: 2 additions & 1 deletion lib/l10n/app_zh_tw.arb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"askAi": "詢問 AI",
"askAiApiKey": "API 金鑰",
"askAiAwaitingResponse": "等待 AI 回應...",
"askAiBaseUrl": "基礎 URL",
"askAiBaseUrl": "API 介面位址",
"askAiEndpointTip": "填寫完整的 Chat Completions 位址,或填寫服務根位址。若位址以 /v1 結尾,應用程式會自動補上 /chat/completions。",
"askAiCommandInserted": "指令已插入終端機",
"askAiConfigMissing": "請前往設定配置 {fields}",
"askAiConfirmExecute": "執行前確認",
Expand Down
Loading