feat(coding): complete PI conversation UI
This commit is contained in:
@@ -1,8 +1,15 @@
|
||||
import type {
|
||||
CodingRuntimeDiagnostics,
|
||||
ConversationInteraction,
|
||||
ConversationInteractionResponse,
|
||||
ConversationModelState,
|
||||
ConversationSnapshot,
|
||||
ConversationThinkingLevel,
|
||||
ProductModelRef,
|
||||
PromptAcceptance,
|
||||
PromptMode,
|
||||
} from '@/types/coding-conversation';
|
||||
import type { CodingConversationMetadata } from '@/types/coding-project';
|
||||
import {
|
||||
createHostEventSource,
|
||||
ensureHostApiToken,
|
||||
@@ -53,6 +60,89 @@ export async function recoverCodingConversation(
|
||||
);
|
||||
}
|
||||
|
||||
export async function abortCodingConversation(conversationId: string): Promise<void> {
|
||||
await hostApiFetch(
|
||||
`/api/coding/conversations/${encodeURIComponent(conversationId)}/abort`,
|
||||
{ method: 'POST', body: '{}' },
|
||||
);
|
||||
}
|
||||
|
||||
export async function setCodingConversationModel(
|
||||
conversationId: string,
|
||||
model: ProductModelRef,
|
||||
): Promise<ConversationModelState> {
|
||||
const response = await hostApiFetch<{ model: ConversationModelState }>(
|
||||
`/api/coding/conversations/${encodeURIComponent(conversationId)}/model`,
|
||||
{ method: 'POST', body: JSON.stringify({ model }) },
|
||||
);
|
||||
return response.model;
|
||||
}
|
||||
|
||||
export async function setCodingConversationThinking(
|
||||
conversationId: string,
|
||||
thinkingLevel: ConversationThinkingLevel,
|
||||
): Promise<ConversationModelState> {
|
||||
const response = await hostApiFetch<{ model: ConversationModelState }>(
|
||||
`/api/coding/conversations/${encodeURIComponent(conversationId)}/thinking`,
|
||||
{ method: 'POST', body: JSON.stringify({ thinkingLevel }) },
|
||||
);
|
||||
return response.model;
|
||||
}
|
||||
|
||||
export async function compactCodingConversation(conversationId: string): Promise<void> {
|
||||
await hostApiFetch(
|
||||
`/api/coding/conversations/${encodeURIComponent(conversationId)}/compact`,
|
||||
{ method: 'POST', body: '{}' },
|
||||
);
|
||||
}
|
||||
|
||||
export async function forkCodingConversation(
|
||||
conversationId: string,
|
||||
sourceEntryId?: string,
|
||||
): Promise<CodingConversationMetadata> {
|
||||
const response = await hostApiFetch<{ conversation: CodingConversationMetadata }>(
|
||||
`/api/coding/conversations/${encodeURIComponent(conversationId)}/fork`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify(sourceEntryId ? { sourceEntryId } : {}),
|
||||
},
|
||||
);
|
||||
return response.conversation;
|
||||
}
|
||||
|
||||
export async function listCodingConversationInteractions(
|
||||
conversationId?: string,
|
||||
): Promise<ConversationInteraction[]> {
|
||||
const params = new URLSearchParams();
|
||||
if (conversationId) params.set('conversationId', conversationId);
|
||||
const query = params.toString();
|
||||
const response = await hostApiFetch<{ interactions: ConversationInteraction[] }>(
|
||||
`/api/coding/interactions${query ? `?${query}` : ''}`,
|
||||
);
|
||||
return response.interactions;
|
||||
}
|
||||
|
||||
export async function respondCodingConversationInteraction(
|
||||
conversationId: string,
|
||||
response: ConversationInteractionResponse,
|
||||
): Promise<void> {
|
||||
const { interactionId, ...answer } = response;
|
||||
await hostApiFetch(
|
||||
`/api/coding/interactions/${encodeURIComponent(interactionId)}/respond`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ conversationId, ...answer }),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export async function getCodingRuntimeDiagnostics(): Promise<CodingRuntimeDiagnostics> {
|
||||
const response = await hostApiFetch<{ runtime: CodingRuntimeDiagnostics }>(
|
||||
'/api/coding/runtime/diagnostics',
|
||||
);
|
||||
return response.runtime;
|
||||
}
|
||||
|
||||
export async function openCodingConversationEvents(
|
||||
conversationId?: string,
|
||||
): Promise<EventSource> {
|
||||
|
||||
58
src/lib/coding-model-options.ts
Normal file
58
src/lib/coding-model-options.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import type { ProviderAccount, ProviderVendorInfo } from '@/lib/providers';
|
||||
import type { ProductModelRef } from '@/types/coding-conversation';
|
||||
|
||||
export interface CodingModelOption {
|
||||
key: string;
|
||||
accountId: string;
|
||||
modelId: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export function codingModelKey(model: Pick<ProductModelRef, 'accountId' | 'modelId'>): string {
|
||||
return JSON.stringify([model.accountId, model.modelId]);
|
||||
}
|
||||
|
||||
export function parseCodingModelKey(key: string): Pick<ProductModelRef, 'accountId' | 'modelId'> | null {
|
||||
try {
|
||||
const value = JSON.parse(key) as unknown;
|
||||
if (!Array.isArray(value) || value.length !== 2) return null;
|
||||
const [accountId, modelId] = value;
|
||||
if (typeof accountId !== 'string' || !accountId.trim()
|
||||
|| typeof modelId !== 'string' || !modelId.trim()) return null;
|
||||
return { accountId: accountId.trim(), modelId: modelId.trim() };
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function buildCodingModelOptions(
|
||||
accounts: ProviderAccount[],
|
||||
vendors: ProviderVendorInfo[],
|
||||
): CodingModelOption[] {
|
||||
const vendorNames = new Map(vendors.map((vendor) => [vendor.id, vendor.name]));
|
||||
const seen = new Set<string>();
|
||||
const options: CodingModelOption[] = [];
|
||||
for (const account of accounts) {
|
||||
if (!account.enabled) continue;
|
||||
const modelIds = [
|
||||
account.model,
|
||||
...(account.fallbackModels ?? []),
|
||||
...(account.metadata?.customModels ?? []),
|
||||
];
|
||||
for (const value of modelIds) {
|
||||
const modelId = value?.trim();
|
||||
if (!modelId) continue;
|
||||
const key = codingModelKey({ accountId: account.id, modelId });
|
||||
if (seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
const vendor = vendorNames.get(account.vendorId);
|
||||
options.push({
|
||||
key,
|
||||
accountId: account.id,
|
||||
modelId,
|
||||
label: `${account.label}${vendor && vendor !== account.label ? ` · ${vendor}` : ''} / ${modelId}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
@@ -46,3 +46,21 @@ export async function createCodingProjectConversation(input: {
|
||||
);
|
||||
return response.conversation;
|
||||
}
|
||||
|
||||
export async function patchCodingProjectConversation(
|
||||
conversationId: string,
|
||||
patch: { title?: string; archived?: boolean; unread?: boolean },
|
||||
): Promise<CodingConversationMetadata> {
|
||||
const response = await hostApiFetch<{ conversation: CodingConversationMetadata }>(
|
||||
`/api/coding/conversations/${encodeURIComponent(conversationId)}`,
|
||||
{ method: 'PATCH', body: JSON.stringify(patch) },
|
||||
);
|
||||
return response.conversation;
|
||||
}
|
||||
|
||||
export async function deleteCodingProjectConversation(conversationId: string): Promise<void> {
|
||||
await hostApiFetch(
|
||||
`/api/coding/conversations/${encodeURIComponent(conversationId)}`,
|
||||
{ method: 'DELETE' },
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user