mirror of
https://github.com/melgarafael/DeskcommCRM.git
synced 2026-10-02 01:28:34 +08:00
O debounce de rajada (mensagens do MESMO contato dentro da janela viram uma resposta só) era só a env do worker INBOUND_DEBOUNCE_MS (default 8000, global, invisível na tela, exige VPS). Agora vira campo opcional por agente em 'Freios de segurança': vazio = usa a env da instalação (regressão zero), 0 desliga a coalescência, teto de 60s clampa pelo leitor (debounceEfetivo). O drain lê a config do agente da conversa (loadConversationAgentConfig, mesma resolução do turno) e usa o debounce do agente no lugar do literal. DoD: migration 0498 + apêndice idempotente no baseline + linha no MANIFEST; leitor testado (default/env, campo, teto no limite); gate de consumidor de knob atendido.
77 lines
2.3 KiB
TypeScript
77 lines
2.3 KiB
TypeScript
"use client";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import { apiClient } from "@/lib/api/client";
|
|
|
|
export type Provider = "anthropic" | "openai" | "google";
|
|
|
|
export interface AgentVersionRow {
|
|
id: string;
|
|
organization_id: string;
|
|
agent_id: string;
|
|
version_number: number;
|
|
system_prompt: string;
|
|
provider: Provider;
|
|
model: string;
|
|
credential_id: string;
|
|
tool_ids: string[];
|
|
trigger_config: Record<string, unknown> | null;
|
|
channel_session_id: string;
|
|
max_steps: number;
|
|
token_budget: number;
|
|
cost_budget_cents: number;
|
|
history_message_window: number;
|
|
history_token_window: number;
|
|
handoff_keywords: string[];
|
|
handoff_tool_enabled: boolean;
|
|
proposal_ai_draft_enabled: boolean;
|
|
cases_enabled: boolean;
|
|
operator_enabled: boolean;
|
|
operator_model: string | null;
|
|
operator_tool_ids: string[];
|
|
pipeline_ids: string[];
|
|
knowledge_source_ids: string[];
|
|
split_messages: boolean;
|
|
split_max_chars: number;
|
|
inbound_debounce_ms: number | null;
|
|
followup: {
|
|
enabled: boolean;
|
|
flow_pointer_ids: string[];
|
|
callback_enabled?: boolean;
|
|
send_window?: { start: string; end: string; weekdays: number[] } | null;
|
|
};
|
|
status: "draft" | "published" | "superseded" | "archived";
|
|
published_at: string | null;
|
|
superseded_at: string | null;
|
|
created_at: string;
|
|
created_by: string | null;
|
|
}
|
|
|
|
interface ListResponse {
|
|
data: AgentVersionRow[];
|
|
}
|
|
|
|
export const agentVersionsKey = (agentId: string) =>
|
|
["ai", "agents", agentId, "versions"] as const;
|
|
|
|
export function useAgentVersions(agentId: string, opts?: { initialData?: AgentVersionRow[] }) {
|
|
return useQuery({
|
|
queryKey: agentVersionsKey(agentId),
|
|
queryFn: async () => {
|
|
const res = await apiClient.get<ListResponse>(`/api/v1/ai/agents/${agentId}/versions`);
|
|
return res.data;
|
|
},
|
|
initialData: opts?.initialData,
|
|
enabled: !!agentId,
|
|
});
|
|
}
|
|
|
|
export function pickLatestDraft(versions: AgentVersionRow[]): AgentVersionRow | null {
|
|
const drafts = versions.filter((v) => v.status === "draft");
|
|
if (drafts.length === 0) return null;
|
|
return drafts.reduce((a, b) => (a.version_number > b.version_number ? a : b));
|
|
}
|
|
|
|
export function pickPublished(versions: AgentVersionRow[]): AgentVersionRow | null {
|
|
return versions.find((v) => v.status === "published") ?? null;
|
|
}
|