418 lines
13 KiB
TypeScript
418 lines
13 KiB
TypeScript
import { apiRequest } from "./base";
|
|
|
|
type ApiEnvelope<T> = { code: number; message: string; data: T };
|
|
|
|
function unwrap<T>(raw: ApiEnvelope<T> | T): T {
|
|
if (raw && typeof raw === "object" && "code" in (raw as ApiEnvelope<T>) && "data" in (raw as ApiEnvelope<T>)) {
|
|
return (raw as ApiEnvelope<T>).data;
|
|
}
|
|
return raw as T;
|
|
}
|
|
|
|
export interface ProjectFromTextRequest {
|
|
input_text: string;
|
|
}
|
|
|
|
export interface ProjectFromTextRequirementItem {
|
|
section: string;
|
|
title: string;
|
|
description: string;
|
|
priority?: string;
|
|
source_kind?: string;
|
|
source_name?: string;
|
|
source_identifier?: string;
|
|
}
|
|
|
|
export interface ProjectFromTextSignalSource {
|
|
registry_id: number | string;
|
|
id?: number | string;
|
|
project_id?: number | string;
|
|
signal_id?: number | string | null;
|
|
source_kind: string;
|
|
source_name: string;
|
|
source_identifier: string;
|
|
description?: string | null;
|
|
requested_via?: string;
|
|
status?: string;
|
|
onboarding_status?: string;
|
|
section?: string;
|
|
title?: string;
|
|
priority?: string;
|
|
origin?: string;
|
|
raw_context?: Record<string, unknown> | null;
|
|
config?: Record<string, unknown> | null;
|
|
}
|
|
|
|
export interface ProjectFromTextResponse {
|
|
project: { id: string; name: string; title: string; description: string; status: string };
|
|
signal: { id: string; content: string; impact_score: number };
|
|
charter: { id: string; title: string; content: string | Record<string, unknown> | null };
|
|
tasks: Array<{ id: string; title: string; type: string; estimated_hours: number; priority: string }>;
|
|
risks: Array<{ type: string; description: string; severity: string }>;
|
|
signal_sources: ProjectFromTextSignalSource[];
|
|
analysis: {
|
|
project_name: string;
|
|
project_title?: string;
|
|
tech_stack: string[];
|
|
timeline: string;
|
|
primary_role: string;
|
|
target_audience?: string;
|
|
domain_id?: number;
|
|
key_features?: string[];
|
|
competitors?: string[];
|
|
success_metrics?: string[];
|
|
core_pain_points?: string[];
|
|
mvp_features?: string[];
|
|
requirement_items?: ProjectFromTextRequirementItem[];
|
|
};
|
|
}
|
|
|
|
export interface SignalSourceRepairRequest {
|
|
project_id: number;
|
|
registry_ids: number[];
|
|
sender?: string;
|
|
}
|
|
|
|
export interface SignalSourceRepairItem {
|
|
registry_id: number;
|
|
signal_id: number | null;
|
|
capture_status: string;
|
|
work_item_id: number | null;
|
|
route_status: string;
|
|
}
|
|
|
|
export interface SignalSourceRepairResponse {
|
|
project_id: number;
|
|
items: SignalSourceRepairItem[];
|
|
}
|
|
|
|
export interface CognitiveConflict {
|
|
id: string;
|
|
type: string;
|
|
description: string;
|
|
severity: string;
|
|
related_users: string[];
|
|
}
|
|
|
|
export interface Blocker {
|
|
id: string;
|
|
type: string;
|
|
description: string;
|
|
severity: string;
|
|
detected_at: string;
|
|
}
|
|
|
|
export interface DailyBriefing {
|
|
date: string;
|
|
summary: string;
|
|
highlights: Array<{ type: string; priority: string; content: string }>;
|
|
your_tasks: Array<{ id: number; title: string; status: string; priority: string }>;
|
|
risks: Array<{ type: string; task: string; severity: string }>;
|
|
signals: Array<{ id: number; source: string; content: string; impact_score: number }>;
|
|
conflicts: Array<{ id: string; type: string; description: string }>;
|
|
action_items: Array<{ type: string; content: string; priority: string }>;
|
|
team_updates: Array<{ assignee_id: number; task: string; status: string }>;
|
|
}
|
|
|
|
export interface ExecutiveBriefingMetric {
|
|
label: string;
|
|
value: string | number;
|
|
tone: string;
|
|
}
|
|
|
|
export interface BriefingTarget {
|
|
type: "project_board" | "workload_detail" | "user_profile" | "ai_steward_tab";
|
|
ref: string;
|
|
label?: string | null;
|
|
params?: Record<string, string | number | boolean>;
|
|
}
|
|
|
|
export interface BriefingMetaEntry {
|
|
label: string;
|
|
value: string;
|
|
}
|
|
|
|
export interface ExecutiveBriefingItem {
|
|
id: string;
|
|
title: string;
|
|
detail: string;
|
|
detail_lines: string[];
|
|
meta: BriefingMetaEntry[];
|
|
tone: string;
|
|
kind?: string | null;
|
|
target?: BriefingTarget | null;
|
|
project_id?: number | null;
|
|
project_name?: string | null;
|
|
user_id?: number | null;
|
|
owner_name?: string | null;
|
|
related_user_ids?: number[];
|
|
occurred_at?: string | null;
|
|
}
|
|
|
|
export interface ExecutiveBriefingSection {
|
|
key: string;
|
|
title: string;
|
|
summary: string;
|
|
metrics: ExecutiveBriefingMetric[];
|
|
items: ExecutiveBriefingItem[];
|
|
drilldown_tab?: string | null;
|
|
}
|
|
|
|
export interface ExecutiveBriefingHero {
|
|
title: string;
|
|
decision_count: number;
|
|
anomaly_count: number;
|
|
baseline_generated_at?: string | null;
|
|
live_generated_at?: string | null;
|
|
key_alerts: ExecutiveBriefingMetric[];
|
|
snapshot_state: "ready" | "missing" | string;
|
|
}
|
|
|
|
export interface ExecutiveBriefingConfig {
|
|
enabled: boolean;
|
|
send_time: string;
|
|
send_cycle: "workdays" | "daily";
|
|
delivery_target_type: "management_group";
|
|
delivery_target_ref: string | null;
|
|
timezone: string;
|
|
updated_at?: string | null;
|
|
updated_by?: number | null;
|
|
}
|
|
|
|
export interface ExecutiveBriefingPayload {
|
|
brief_date: string;
|
|
mode: "snapshot" | "live";
|
|
summary: Record<string, unknown>;
|
|
hero: ExecutiveBriefingHero;
|
|
sections: ExecutiveBriefingSection[];
|
|
delivery: ExecutiveBriefingConfig;
|
|
}
|
|
|
|
export interface DailyBriefingV2PersonalSummary {
|
|
headline: string;
|
|
action_count: number;
|
|
project_count: number;
|
|
}
|
|
|
|
export interface DailyBriefingV2PersonalWorkload {
|
|
user_id: number;
|
|
user_name: string;
|
|
assigned_hours: number;
|
|
max_weekly_hours: number;
|
|
workload_ratio: number;
|
|
tone: string;
|
|
}
|
|
|
|
export interface DailyBriefingV2Payload {
|
|
brief_date: string;
|
|
mode: "snapshot" | "live";
|
|
viewer_scope: "tenant_full" | "personal";
|
|
hero: ExecutiveBriefingHero;
|
|
sections: ExecutiveBriefingSection[];
|
|
personal_summary?: DailyBriefingV2PersonalSummary | null;
|
|
personal_workload?: DailyBriefingV2PersonalWorkload | null;
|
|
export_title: string;
|
|
}
|
|
|
|
export type LLMProvider = "deepseek" | "glm" | "openai" | "ollama";
|
|
|
|
export interface LLMProviderPreset {
|
|
model: string;
|
|
base_url: string;
|
|
api_env: string;
|
|
}
|
|
|
|
export interface LLMProviderPresetResponse {
|
|
low: string;
|
|
high: string;
|
|
base_url: string;
|
|
providers: Record<LLMProvider, LLMProviderPreset>;
|
|
}
|
|
|
|
export interface LLMEngineSettings {
|
|
enable_local_kb_qa: boolean;
|
|
local_kb_qa_scope: "kb_qa_only";
|
|
}
|
|
|
|
export interface LLMEngine {
|
|
id: string;
|
|
name: string;
|
|
provider: LLMProvider;
|
|
model: string;
|
|
base_url: string;
|
|
api_key_masked: string;
|
|
updated_at?: string;
|
|
created_at?: string;
|
|
}
|
|
|
|
export interface LLMEngineListResponse {
|
|
active_engine_id: string | null;
|
|
engines: LLMEngine[];
|
|
settings: LLMEngineSettings;
|
|
}
|
|
|
|
export interface LLMEngineUpsertRequest {
|
|
name: string;
|
|
provider: LLMProvider;
|
|
api_key?: string;
|
|
model?: string;
|
|
base_url?: string;
|
|
set_active?: boolean;
|
|
}
|
|
|
|
export interface LLMEngineConnectionTestResponse {
|
|
ok: boolean;
|
|
engine_id: string;
|
|
name: string;
|
|
provider: LLMProvider;
|
|
model: string;
|
|
base_url: string;
|
|
api_key_masked: string;
|
|
latency_ms: number;
|
|
response_preview: string;
|
|
}
|
|
|
|
export const aiStewardApi = {
|
|
async listCognitiveConflicts(projectId?: number): Promise<CognitiveConflict[]> {
|
|
const query = typeof projectId === "number" ? `?project_id=${projectId}` : "";
|
|
return await apiRequest<CognitiveConflict[]>(`/ai-steward/cognitive-conflicts${query}`);
|
|
},
|
|
async detectConflicts(projectId: number): Promise<{ project_id: number; detected_count: number; conflicts: CognitiveConflict[] }> {
|
|
return await apiRequest<{ project_id: number; detected_count: number; conflicts: CognitiveConflict[] }>(
|
|
"/ai-steward/conflicts/detect",
|
|
{
|
|
method: "POST",
|
|
body: JSON.stringify({ project_id: projectId, detect_type: "all" }),
|
|
}
|
|
);
|
|
},
|
|
async listActiveBlockers(): Promise<{ total: number; blockers: Blocker[] }> {
|
|
return await apiRequest<{ total: number; blockers: Blocker[] }>("/ai-steward/blockers/active");
|
|
},
|
|
async detectTaskBlockers(): Promise<{ total_detected: number; blockers: Blocker[] }> {
|
|
return await apiRequest<{ total_detected: number; blockers: Blocker[] }>("/ai-steward/blockers/detect-tasks", {
|
|
method: "POST",
|
|
});
|
|
},
|
|
async getDailyBriefing(): Promise<DailyBriefing> {
|
|
return await apiRequest<DailyBriefing>("/ai-steward/daily-briefing");
|
|
},
|
|
async getDailyBriefingV2(mode: "snapshot" | "live"): Promise<DailyBriefingV2Payload> {
|
|
const raw = await apiRequest<ApiEnvelope<DailyBriefingV2Payload>>(`/ai-steward/daily-briefing-v2?mode=${mode}`);
|
|
return unwrap(raw);
|
|
},
|
|
async startHnMonitor(intervalMinutes: number = 30): Promise<Record<string, unknown>> {
|
|
return await apiRequest<Record<string, unknown>>(
|
|
`/ai-steward/signal/start-hn-monitor?interval_minutes=${intervalMinutes}`,
|
|
{
|
|
method: "POST",
|
|
}
|
|
);
|
|
},
|
|
async getExecutiveBriefing(mode: "snapshot" | "live"): Promise<ExecutiveBriefingPayload> {
|
|
const raw = await apiRequest<ApiEnvelope<ExecutiveBriefingPayload>>(`/ai-steward/executive-briefing?mode=${mode}`);
|
|
return unwrap(raw);
|
|
},
|
|
async getExecutiveBriefingConfig(): Promise<ExecutiveBriefingConfig> {
|
|
const raw = await apiRequest<ApiEnvelope<ExecutiveBriefingConfig>>("/ai-steward/executive-briefing/config");
|
|
return unwrap(raw);
|
|
},
|
|
async updateExecutiveBriefingConfig(body: ExecutiveBriefingConfig): Promise<ExecutiveBriefingConfig> {
|
|
const raw = await apiRequest<ApiEnvelope<ExecutiveBriefingConfig>>("/ai-steward/executive-briefing/config", {
|
|
method: "PUT",
|
|
body: JSON.stringify(body),
|
|
});
|
|
return unwrap(raw);
|
|
},
|
|
async getLlmProviderPresets(): Promise<LLMProviderPresetResponse> {
|
|
const raw = await apiRequest<ApiEnvelope<LLMProviderPresetResponse>>("/ai-steward/llm/providers");
|
|
return unwrap(raw);
|
|
},
|
|
async listLlmEngines(): Promise<LLMEngineListResponse> {
|
|
const raw = await apiRequest<ApiEnvelope<LLMEngineListResponse>>("/ai-steward/llm-engines");
|
|
return unwrap(raw);
|
|
},
|
|
async createLlmEngine(body: LLMEngineUpsertRequest): Promise<LLMEngineListResponse & { engine: LLMEngine }> {
|
|
const raw = await apiRequest<ApiEnvelope<LLMEngineListResponse & { engine: LLMEngine }>>("/ai-steward/llm-engines", {
|
|
method: "POST",
|
|
body: JSON.stringify(body),
|
|
});
|
|
return unwrap(raw);
|
|
},
|
|
async updateLlmEngine(
|
|
engineId: string,
|
|
body: Partial<LLMEngineUpsertRequest>,
|
|
): Promise<LLMEngineListResponse & { engine: LLMEngine }> {
|
|
const raw = await apiRequest<ApiEnvelope<LLMEngineListResponse & { engine: LLMEngine }>>(
|
|
`/ai-steward/llm-engines/${engineId}`,
|
|
{
|
|
method: "PUT",
|
|
body: JSON.stringify(body),
|
|
}
|
|
);
|
|
return unwrap(raw);
|
|
},
|
|
async activateLlmEngine(engineId: string): Promise<LLMEngineListResponse & { active_engine_id: string }> {
|
|
const raw = await apiRequest<ApiEnvelope<LLMEngineListResponse & { active_engine_id: string }>>(
|
|
`/ai-steward/llm-engines/${engineId}/activate`,
|
|
{
|
|
method: "POST",
|
|
}
|
|
);
|
|
return unwrap(raw);
|
|
},
|
|
async deleteLlmEngine(
|
|
engineId: string,
|
|
): Promise<LLMEngineListResponse & { deleted_engine_id: string; remaining_count: number }> {
|
|
const raw = await apiRequest<
|
|
ApiEnvelope<LLMEngineListResponse & { deleted_engine_id: string; remaining_count: number }>
|
|
>(`/ai-steward/llm-engines/${engineId}`, {
|
|
method: "DELETE",
|
|
});
|
|
return unwrap(raw);
|
|
},
|
|
async updateLlmEngineSettings(
|
|
body: Pick<LLMEngineSettings, "enable_local_kb_qa">,
|
|
): Promise<LLMEngineListResponse & { settings: LLMEngineSettings }> {
|
|
const raw = await apiRequest<ApiEnvelope<LLMEngineListResponse & { settings: LLMEngineSettings }>>(
|
|
"/ai-steward/llm-engines/settings",
|
|
{
|
|
method: "PUT",
|
|
body: JSON.stringify(body),
|
|
}
|
|
);
|
|
return unwrap(raw);
|
|
},
|
|
async testActiveLlmEngine(): Promise<LLMEngineConnectionTestResponse> {
|
|
const raw = await apiRequest<ApiEnvelope<LLMEngineConnectionTestResponse>>("/ai-steward/llm-engines/test-active", {
|
|
method: "POST",
|
|
body: JSON.stringify({}),
|
|
});
|
|
return unwrap(raw);
|
|
},
|
|
async testLlmEngine(engineId: string): Promise<LLMEngineConnectionTestResponse> {
|
|
const raw = await apiRequest<ApiEnvelope<LLMEngineConnectionTestResponse>>(
|
|
`/ai-steward/llm-engines/${engineId}/test`,
|
|
{
|
|
method: "POST",
|
|
body: JSON.stringify({}),
|
|
}
|
|
);
|
|
return unwrap(raw);
|
|
},
|
|
async repairDirtySignalSources(body: SignalSourceRepairRequest): Promise<SignalSourceRepairResponse> {
|
|
const raw = await apiRequest<ApiEnvelope<SignalSourceRepairResponse>>("/ai-steward/signal-sources/repair-dirty", {
|
|
method: "POST",
|
|
body: JSON.stringify(body),
|
|
});
|
|
return unwrap(raw);
|
|
},
|
|
async createProjectFromText(body: ProjectFromTextRequest): Promise<ProjectFromTextResponse> {
|
|
const raw = await apiRequest<ApiEnvelope<ProjectFromTextResponse>>("/ai-steward/project-from-text", {
|
|
method: "POST",
|
|
body: JSON.stringify(body),
|
|
});
|
|
return unwrap(raw);
|
|
},
|
|
};
|