Merge branch 'develop' of https://git.muticolturano.com/Adiuva/adiuva into develop
This commit is contained in:
@@ -86,7 +86,7 @@ async function tickAgentScheduler(): Promise<void> {
|
|||||||
agentId: agent.id,
|
agentId: agent.id,
|
||||||
whatToExtract: agent.dataTypes,
|
whatToExtract: agent.dataTypes,
|
||||||
batchInterval: agent.scheduleCron,
|
batchInterval: agent.scheduleCron,
|
||||||
customAgentPrompt: agent.promptTemplate,
|
agentConfig: agent.agentConfig ?? undefined,
|
||||||
activeAgents,
|
activeAgents,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ interface StreamListener {
|
|||||||
|
|
||||||
/** Pending journey reply listener — resolves when a `journey_reply` arrives. */
|
/** Pending journey reply listener — resolves when a `journey_reply` arrives. */
|
||||||
interface JourneyListener {
|
interface JourneyListener {
|
||||||
resolve: (reply: { sessionId: string; message: string; done: boolean; promptTemplate?: string | null }) => void;
|
resolve: (reply: { sessionId: string; message: string; done: boolean; agentConfig?: string | null }) => void;
|
||||||
reject: (err: Error) => void;
|
reject: (err: Error) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,8 +384,8 @@ export class BackendClient {
|
|||||||
agentType: 'local_directory' | 'gmail' | 'teams' | 'outlook',
|
agentType: 'local_directory' | 'gmail' | 'teams' | 'outlook',
|
||||||
dataTypes: string[],
|
dataTypes: string[],
|
||||||
directory?: string,
|
directory?: string,
|
||||||
existingTemplate?: string | null,
|
existingConfig?: string | null,
|
||||||
): Promise<{ sessionId: string; message: string; done: boolean; promptTemplate?: string | null }> {
|
): Promise<{ sessionId: string; message: string; done: boolean; agentConfig?: string | null }> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.journeyListeners.set(sessionId, { resolve, reject });
|
this.journeyListeners.set(sessionId, { resolve, reject });
|
||||||
|
|
||||||
@@ -402,7 +402,7 @@ export class BackendClient {
|
|||||||
agentType,
|
agentType,
|
||||||
directory: directory ?? null,
|
directory: directory ?? null,
|
||||||
dataTypes,
|
dataTypes,
|
||||||
existingTemplate: existingTemplate ?? null,
|
existingConfig: existingConfig ?? null,
|
||||||
});
|
});
|
||||||
logWsSend(payload);
|
logWsSend(payload);
|
||||||
ws.send(JSON.stringify(payload));
|
ws.send(JSON.stringify(payload));
|
||||||
@@ -417,7 +417,7 @@ export class BackendClient {
|
|||||||
sendJourneyMessage(
|
sendJourneyMessage(
|
||||||
sessionId: string,
|
sessionId: string,
|
||||||
message: string,
|
message: string,
|
||||||
): Promise<{ sessionId: string; message: string; done: boolean; promptTemplate?: string | null }> {
|
): Promise<{ sessionId: string; message: string; done: boolean; agentConfig?: string | null }> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.journeyListeners.set(sessionId, { resolve, reject });
|
this.journeyListeners.set(sessionId, { resolve, reject });
|
||||||
|
|
||||||
@@ -730,7 +730,7 @@ export class BackendClient {
|
|||||||
sessionId: frame.data.sessionId,
|
sessionId: frame.data.sessionId,
|
||||||
message: frame.data.message,
|
message: frame.data.message,
|
||||||
done: frame.data.done,
|
done: frame.data.done,
|
||||||
promptTemplate: frame.data.promptTemplate,
|
agentConfig: frame.data.agentConfig,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -707,7 +707,7 @@ const agentLocalRouter = router({
|
|||||||
name: z.string(),
|
name: z.string(),
|
||||||
directory: z.string(),
|
directory: z.string(),
|
||||||
dataTypes: z.array(z.string()),
|
dataTypes: z.array(z.string()),
|
||||||
promptTemplate: z.string(),
|
agentConfig: z.record(z.string(), z.unknown()).nullable().optional(),
|
||||||
scheduleCron: z.string(),
|
scheduleCron: z.string(),
|
||||||
}))
|
}))
|
||||||
.mutation(({ input }) => {
|
.mutation(({ input }) => {
|
||||||
@@ -716,7 +716,7 @@ const agentLocalRouter = router({
|
|||||||
name: input.name,
|
name: input.name,
|
||||||
directory: input.directory,
|
directory: input.directory,
|
||||||
dataTypes: input.dataTypes,
|
dataTypes: input.dataTypes,
|
||||||
promptTemplate: input.promptTemplate,
|
agentConfig: input.agentConfig ?? null,
|
||||||
scheduleCron: input.scheduleCron,
|
scheduleCron: input.scheduleCron,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
lastRunAt: null,
|
lastRunAt: null,
|
||||||
@@ -731,7 +731,7 @@ const agentLocalRouter = router({
|
|||||||
name: z.string().optional(),
|
name: z.string().optional(),
|
||||||
directory: z.string().optional(),
|
directory: z.string().optional(),
|
||||||
dataTypes: z.array(z.string()).optional(),
|
dataTypes: z.array(z.string()).optional(),
|
||||||
promptTemplate: z.string().optional(),
|
agentConfig: z.record(z.string(), z.unknown()).nullable().optional(),
|
||||||
scheduleCron: z.string().optional(),
|
scheduleCron: z.string().optional(),
|
||||||
enabled: z.boolean().optional(),
|
enabled: z.boolean().optional(),
|
||||||
}))
|
}))
|
||||||
@@ -745,7 +745,7 @@ const agentLocalRouter = router({
|
|||||||
...(input.name !== undefined && { name: input.name }),
|
...(input.name !== undefined && { name: input.name }),
|
||||||
...(input.directory !== undefined && { directory: input.directory }),
|
...(input.directory !== undefined && { directory: input.directory }),
|
||||||
...(input.dataTypes !== undefined && { dataTypes: input.dataTypes }),
|
...(input.dataTypes !== undefined && { dataTypes: input.dataTypes }),
|
||||||
...(input.promptTemplate !== undefined && { promptTemplate: input.promptTemplate }),
|
...('agentConfig' in input && { agentConfig: input.agentConfig ?? null }),
|
||||||
...(input.scheduleCron !== undefined && { scheduleCron: input.scheduleCron }),
|
...(input.scheduleCron !== undefined && { scheduleCron: input.scheduleCron }),
|
||||||
...(input.enabled !== undefined && { enabled: input.enabled }),
|
...(input.enabled !== undefined && { enabled: input.enabled }),
|
||||||
};
|
};
|
||||||
@@ -837,7 +837,7 @@ const agentJourneyRouter = router({
|
|||||||
agentType: z.enum(['local_directory', 'gmail', 'teams', 'outlook']),
|
agentType: z.enum(['local_directory', 'gmail', 'teams', 'outlook']),
|
||||||
dataTypes: z.array(z.string()),
|
dataTypes: z.array(z.string()),
|
||||||
directory: z.string().optional(),
|
directory: z.string().optional(),
|
||||||
existingTemplate: z.string().optional(),
|
existingConfig: z.string().optional(),
|
||||||
}))
|
}))
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input }) => {
|
||||||
try {
|
try {
|
||||||
@@ -847,9 +847,9 @@ const agentJourneyRouter = router({
|
|||||||
input.agentType,
|
input.agentType,
|
||||||
input.dataTypes,
|
input.dataTypes,
|
||||||
input.directory,
|
input.directory,
|
||||||
input.existingTemplate,
|
input.existingConfig,
|
||||||
);
|
);
|
||||||
return { data: { sessionId: result.sessionId, message: result.message, done: result.done, promptTemplate: result.promptTemplate ?? undefined }, error: null };
|
return { data: { sessionId: result.sessionId, message: result.message, done: result.done, agentConfig: result.agentConfig ?? undefined }, error: null };
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const msg = err instanceof Error ? err.message : 'Failed to start journey';
|
const msg = err instanceof Error ? err.message : 'Failed to start journey';
|
||||||
return { data: null, error: msg };
|
return { data: null, error: msg };
|
||||||
@@ -861,7 +861,7 @@ const agentJourneyRouter = router({
|
|||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input }) => {
|
||||||
try {
|
try {
|
||||||
const result = await getBackendClient().sendJourneyMessage(input.sessionId, input.message);
|
const result = await getBackendClient().sendJourneyMessage(input.sessionId, input.message);
|
||||||
return { data: { sessionId: result.sessionId, message: result.message, done: result.done, promptTemplate: result.promptTemplate ?? undefined }, error: null };
|
return { data: { sessionId: result.sessionId, message: result.message, done: result.done, agentConfig: result.agentConfig ?? undefined }, error: null };
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const msg = err instanceof Error ? err.message : 'Failed to send journey message';
|
const msg = err instanceof Error ? err.message : 'Failed to send journey message';
|
||||||
return { data: null, error: msg };
|
return { data: null, error: msg };
|
||||||
@@ -981,7 +981,7 @@ const agentRouter = router({
|
|||||||
agentId: agent.id,
|
agentId: agent.id,
|
||||||
whatToExtract: agent.dataTypes,
|
whatToExtract: agent.dataTypes,
|
||||||
batchInterval: agent.scheduleCron,
|
batchInterval: agent.scheduleCron,
|
||||||
customAgentPrompt: agent.promptTemplate,
|
agentConfig: agent.agentConfig ?? undefined,
|
||||||
activeAgents,
|
activeAgents,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ export interface LocalAgentLocalConfig {
|
|||||||
name: string;
|
name: string;
|
||||||
directory: string;
|
directory: string;
|
||||||
dataTypes: string[];
|
dataTypes: string[];
|
||||||
promptTemplate: string;
|
/** Structured extraction config produced by the Journey setup flow. */
|
||||||
|
agentConfig: Record<string, unknown> | null;
|
||||||
scheduleCron: string;
|
scheduleCron: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
lastRunAt: number | null;
|
lastRunAt: number | null;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export function AgentsSection() {
|
|||||||
|
|
||||||
const [expandedAgent, setExpandedAgent] = useState<string | null>(null);
|
const [expandedAgent, setExpandedAgent] = useState<string | null>(null);
|
||||||
const [showTemplatePicker, setShowTemplatePicker] = useState(false);
|
const [showTemplatePicker, setShowTemplatePicker] = useState(false);
|
||||||
const [journeyAgent, setJourneyAgent] = useState<{ id: string; type: 'local' | 'cloud'; name: string; currentPrompt: string; dataTypes: string[]; directory?: string } | null>(null);
|
const [journeyAgent, setJourneyAgent] = useState<{ id: string; type: 'local' | 'cloud'; name: string; currentConfig: Record<string, unknown> | null; dataTypes: string[]; directory?: string } | null>(null);
|
||||||
|
|
||||||
const catalogQuery = trpc.agent.catalog.useQuery(undefined, {
|
const catalogQuery = trpc.agent.catalog.useQuery(undefined, {
|
||||||
enabled: showTemplatePicker,
|
enabled: showTemplatePicker,
|
||||||
@@ -116,7 +116,7 @@ export function AgentsSection() {
|
|||||||
id: agent.id,
|
id: agent.id,
|
||||||
type: agent.agentType,
|
type: agent.agentType,
|
||||||
name: agent.name,
|
name: agent.name,
|
||||||
currentPrompt: agent.promptTemplate,
|
currentConfig: agent.agentType === 'local' ? (agent as LocalAgentConfig).agentConfig ?? null : null,
|
||||||
dataTypes: agent.dataTypes,
|
dataTypes: agent.dataTypes,
|
||||||
directory: agent.agentType === 'local' ? (agent as LocalAgentConfig).directory : undefined,
|
directory: agent.agentType === 'local' ? (agent as LocalAgentConfig).directory : undefined,
|
||||||
})}
|
})}
|
||||||
@@ -145,27 +145,19 @@ export function AgentsSection() {
|
|||||||
<JourneyDialog
|
<JourneyDialog
|
||||||
agentType={journeyAgent.type}
|
agentType={journeyAgent.type}
|
||||||
agentName={journeyAgent.name}
|
agentName={journeyAgent.name}
|
||||||
currentPrompt={journeyAgent.currentPrompt}
|
currentConfig={journeyAgent.currentConfig}
|
||||||
dataTypes={journeyAgent.dataTypes}
|
dataTypes={journeyAgent.dataTypes}
|
||||||
directory={journeyAgent.directory}
|
directory={journeyAgent.directory}
|
||||||
onClose={() => setJourneyAgent(null)}
|
onClose={() => setJourneyAgent(null)}
|
||||||
onSaved={(promptTemplate) => {
|
onSaved={(agentConfig) => {
|
||||||
const local = localAgents.find(a => a.id === journeyAgent.id);
|
const local = localAgents.find(a => a.id === journeyAgent.id);
|
||||||
const cloud = cloudAgents.find(a => a.id === journeyAgent.id);
|
|
||||||
if (local) {
|
if (local) {
|
||||||
updateLocalMutation.mutate({ id: journeyAgent.id, promptTemplate }, {
|
updateLocalMutation.mutate({ id: journeyAgent.id, agentConfig }, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
void utils.agent.local.list.invalidate();
|
void utils.agent.local.list.invalidate();
|
||||||
setJourneyAgent(null);
|
setJourneyAgent(null);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else if (cloud) {
|
|
||||||
updateCloudMutation.mutate({ id: journeyAgent.id, promptTemplate }, {
|
|
||||||
onSuccess: () => {
|
|
||||||
void utils.agent.cloud.list.invalidate();
|
|
||||||
setJourneyAgent(null);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
setJourneyAgent(null);
|
setJourneyAgent(null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ export function InlineAgentCreationStepper({
|
|||||||
const [dataTypes, setDataTypes] = useState<string[]>([]);
|
const [dataTypes, setDataTypes] = useState<string[]>([]);
|
||||||
const [schedule, setSchedule] = useState('0 * * * *');
|
const [schedule, setSchedule] = useState('0 * * * *');
|
||||||
const [promptTemplate, setPromptTemplate] = useState('');
|
const [promptTemplate, setPromptTemplate] = useState('');
|
||||||
|
const [agentConfig, setAgentConfig] = useState<Record<string, unknown> | null>(null);
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
|
|
||||||
const isSubmitting = createLocalMutation.isPending || createCloudMutation.isPending;
|
const isSubmitting = createLocalMutation.isPending || createCloudMutation.isPending;
|
||||||
@@ -49,6 +50,7 @@ export function InlineAgentCreationStepper({
|
|||||||
setDataTypes((item.supportedDataTypes ?? []).slice(0, 2));
|
setDataTypes((item.supportedDataTypes ?? []).slice(0, 2));
|
||||||
setSchedule('0 * * * *');
|
setSchedule('0 * * * *');
|
||||||
setPromptTemplate('');
|
setPromptTemplate('');
|
||||||
|
setAgentConfig(null);
|
||||||
setError('');
|
setError('');
|
||||||
setStep(2);
|
setStep(2);
|
||||||
}
|
}
|
||||||
@@ -103,7 +105,7 @@ export function InlineAgentCreationStepper({
|
|||||||
directory,
|
directory,
|
||||||
dataTypes,
|
dataTypes,
|
||||||
scheduleCron: schedule,
|
scheduleCron: schedule,
|
||||||
promptTemplate,
|
agentConfig: agentConfig ?? null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: () => onCreated(),
|
onSuccess: () => onCreated(),
|
||||||
@@ -269,6 +271,7 @@ export function InlineAgentCreationStepper({
|
|||||||
dataTypes={dataTypes}
|
dataTypes={dataTypes}
|
||||||
directory={selectedTemplate.type === 'local_directory' ? directory : undefined}
|
directory={selectedTemplate.type === 'local_directory' ? directory : undefined}
|
||||||
onPromptUpdate={(p) => setPromptTemplate(p)}
|
onPromptUpdate={(p) => setPromptTemplate(p)}
|
||||||
|
onConfigUpdate={(c) => setAgentConfig(c)}
|
||||||
/>
|
/>
|
||||||
</div>}
|
</div>}
|
||||||
</div>
|
</div>
|
||||||
@@ -298,7 +301,9 @@ export function InlineAgentCreationStepper({
|
|||||||
{selectedTemplate.type === 'local_directory' && directory && (
|
{selectedTemplate.type === 'local_directory' && directory && (
|
||||||
<p><span className="text-muted-foreground">Directory:</span> {directory}</p>
|
<p><span className="text-muted-foreground">Directory:</span> {directory}</p>
|
||||||
)}
|
)}
|
||||||
{promptTemplate && <p><span className="text-muted-foreground">Custom prompt:</span> Added</p>}
|
{(selectedTemplate.type === 'local_directory' ? agentConfig : promptTemplate) && (
|
||||||
|
<p><span className="text-muted-foreground">Extraction config:</span> Added</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -20,10 +20,43 @@ interface JourneyMessage {
|
|||||||
content: string;
|
content: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Parse a JSON string safely — returns null on failure. */
|
||||||
|
function parseAgentConfig(raw: string): Record<string, unknown> | null {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(raw);
|
||||||
|
return parsed && typeof parsed === 'object' ? (parsed as Record<string, unknown>) : null;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Render a short human-readable summary of an AgentConfig object. */
|
||||||
|
function AgentConfigSummary({ config }: { config: Record<string, unknown> }) {
|
||||||
|
const contentTypes = config.content_types as { id?: string; label?: string }[] | undefined;
|
||||||
|
const dataTypes = config.data_types as string[] | undefined;
|
||||||
|
const globalRules = config.global_rules as string[] | undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="text-xs leading-relaxed space-y-1">
|
||||||
|
{dataTypes && dataTypes.length > 0 && (
|
||||||
|
<p><span className="font-medium">Extracts:</span> {dataTypes.join(', ')}</p>
|
||||||
|
)}
|
||||||
|
{contentTypes && contentTypes.length > 0 && (
|
||||||
|
<p><span className="font-medium">Content types:</span>{' '}
|
||||||
|
{contentTypes.map(ct => ct.label ?? ct.id).join(', ')}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{globalRules && globalRules.length > 0 && (
|
||||||
|
<p><span className="font-medium">Rules:</span> {globalRules.length} global rule{globalRules.length !== 1 ? 's' : ''}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function JourneyDialog({
|
export function JourneyDialog({
|
||||||
agentType,
|
agentType,
|
||||||
agentName,
|
agentName,
|
||||||
currentPrompt,
|
currentConfig,
|
||||||
dataTypes,
|
dataTypes,
|
||||||
directory,
|
directory,
|
||||||
onClose,
|
onClose,
|
||||||
@@ -31,11 +64,11 @@ export function JourneyDialog({
|
|||||||
}: {
|
}: {
|
||||||
agentType: 'local' | 'cloud';
|
agentType: 'local' | 'cloud';
|
||||||
agentName: string;
|
agentName: string;
|
||||||
currentPrompt: string;
|
currentConfig: Record<string, unknown> | null;
|
||||||
dataTypes: string[];
|
dataTypes: string[];
|
||||||
directory?: string;
|
directory?: string;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onSaved: (promptTemplate: string) => void;
|
onSaved: (agentConfig: Record<string, unknown>) => void;
|
||||||
}) {
|
}) {
|
||||||
const startMutation = trpc.agent.journey.start.useMutation();
|
const startMutation = trpc.agent.journey.start.useMutation();
|
||||||
const messageMutation = trpc.agent.journey.message.useMutation();
|
const messageMutation = trpc.agent.journey.message.useMutation();
|
||||||
@@ -43,7 +76,7 @@ export function JourneyDialog({
|
|||||||
const [sessionId, setSessionId] = useState<string | null>(null);
|
const [sessionId, setSessionId] = useState<string | null>(null);
|
||||||
const [messages, setMessages] = useState<JourneyMessage[]>([]);
|
const [messages, setMessages] = useState<JourneyMessage[]>([]);
|
||||||
const [input, setInput] = useState('');
|
const [input, setInput] = useState('');
|
||||||
const [finalPrompt, setFinalPrompt] = useState<string | null>(currentPrompt || null);
|
const [finalConfig, setFinalConfig] = useState<Record<string, unknown> | null>(currentConfig);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [isDone, setIsDone] = useState(false);
|
const [isDone, setIsDone] = useState(false);
|
||||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||||
@@ -55,23 +88,31 @@ export function JourneyDialog({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
startMutation.mutate(
|
startMutation.mutate(
|
||||||
{ agentType: journeyAgentType, dataTypes, directory, existingTemplate: currentPrompt || undefined },
|
{
|
||||||
|
agentType: journeyAgentType,
|
||||||
|
dataTypes,
|
||||||
|
directory,
|
||||||
|
existingConfig: currentConfig ? JSON.stringify(currentConfig) : undefined,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
onSuccess: (res) => {
|
onSuccess: (res) => {
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
setSessionId(res.data.sessionId);
|
setSessionId(res.data.sessionId);
|
||||||
setMessages([{ role: 'assistant', content: res.data.message }]);
|
setMessages([{ role: 'assistant', content: res.data.message }]);
|
||||||
if (res.data.done && res.data.promptTemplate) {
|
if (res.data.done && res.data.agentConfig) {
|
||||||
setFinalPrompt(res.data.promptTemplate);
|
const parsed = parseAgentConfig(res.data.agentConfig);
|
||||||
|
if (parsed) {
|
||||||
|
setFinalConfig(parsed);
|
||||||
setIsDone(true);
|
setIsDone(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
},
|
},
|
||||||
onError: () => {
|
onError: () => {
|
||||||
setMessages([{
|
setMessages([{
|
||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
content: "Hi! I'll help you set up your AI prompt. What kinds of information should the agent extract from your files? For example: task titles, due dates, project names, notes.",
|
content: "Hi! I'll help you set up your agent configuration. What kinds of files will this agent process, and what should it extract from them?",
|
||||||
}]);
|
}]);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
},
|
},
|
||||||
@@ -97,10 +138,13 @@ export function JourneyDialog({
|
|||||||
onSuccess: (res) => {
|
onSuccess: (res) => {
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
setMessages(prev => [...prev, { role: 'assistant', content: res.data.message }]);
|
setMessages(prev => [...prev, { role: 'assistant', content: res.data.message }]);
|
||||||
if (res.data.done && res.data.promptTemplate) {
|
if (res.data.done && res.data.agentConfig) {
|
||||||
setFinalPrompt(res.data.promptTemplate);
|
const parsed = parseAgentConfig(res.data.agentConfig);
|
||||||
|
if (parsed) {
|
||||||
|
setFinalConfig(parsed);
|
||||||
setIsDone(true);
|
setIsDone(true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setMessages(prev => [...prev, { role: 'assistant', content: 'Something went wrong. Please try again.' }]);
|
setMessages(prev => [...prev, { role: 'assistant', content: 'Something went wrong. Please try again.' }]);
|
||||||
}
|
}
|
||||||
@@ -120,10 +164,10 @@ export function JourneyDialog({
|
|||||||
<DialogHeader className="px-6 pt-5 pb-4 border-b shrink-0">
|
<DialogHeader className="px-6 pt-5 pb-4 border-b shrink-0">
|
||||||
<DialogTitle className="flex items-center gap-2">
|
<DialogTitle className="flex items-center gap-2">
|
||||||
<Sparkles className="size-4 text-primary" />
|
<Sparkles className="size-4 text-primary" />
|
||||||
Customize AI prompt — {agentName}
|
Configure agent — {agentName}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
Describe what you want your agent to look for — we'll write a tailored prompt for it.
|
Describe what this agent should look for — we'll produce a tailored extraction config.
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
@@ -155,11 +199,11 @@ export function JourneyDialog({
|
|||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
|
||||||
{/* Final prompt preview */}
|
{/* Config preview */}
|
||||||
{finalPrompt && (
|
{finalConfig && (
|
||||||
<div className="mx-6 mb-3 rounded-xl bg-muted/50 border px-4 py-3">
|
<div className="mx-6 mb-3 rounded-xl bg-muted/50 border px-4 py-3">
|
||||||
<p className="text-xs font-medium text-muted-foreground mb-1">Your custom instructions</p>
|
<p className="text-xs font-medium text-muted-foreground mb-1.5">Extraction config ready</p>
|
||||||
<p className="text-xs leading-relaxed line-clamp-4">{finalPrompt}</p>
|
<AgentConfigSummary config={finalConfig} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -168,7 +212,7 @@ export function JourneyDialog({
|
|||||||
{isDone ? (
|
{isDone ? (
|
||||||
<div className="flex items-center gap-2 px-3.5 py-2.5 rounded-xl bg-primary/5 border border-primary/20">
|
<div className="flex items-center gap-2 px-3.5 py-2.5 rounded-xl bg-primary/5 border border-primary/20">
|
||||||
<CheckCircle2 className="size-4 text-primary shrink-0" />
|
<CheckCircle2 className="size-4 text-primary shrink-0" />
|
||||||
<p className="text-sm text-muted-foreground">Prompt ready — click “Save & apply” below.</p>
|
<p className="text-sm text-muted-foreground">Config ready — click “Save & apply” below.</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
@@ -190,8 +234,8 @@ export function JourneyDialog({
|
|||||||
<DialogFooter className="px-6 pb-5 gap-2 shrink-0">
|
<DialogFooter className="px-6 pb-5 gap-2 shrink-0">
|
||||||
<Button variant="outline" onClick={onClose}>Cancel</Button>
|
<Button variant="outline" onClick={onClose}>Cancel</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => onSaved(finalPrompt ?? '')}
|
onClick={() => finalConfig && onSaved(finalConfig)}
|
||||||
disabled={!finalPrompt}
|
disabled={!finalConfig}
|
||||||
>
|
>
|
||||||
Save & apply
|
Save & apply
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -10,11 +10,13 @@ export function PromptBuilderChat({
|
|||||||
dataTypes,
|
dataTypes,
|
||||||
directory,
|
directory,
|
||||||
onPromptUpdate,
|
onPromptUpdate,
|
||||||
|
onConfigUpdate,
|
||||||
}: {
|
}: {
|
||||||
agentType: 'local_directory' | 'gmail' | 'teams' | 'outlook';
|
agentType: 'local_directory' | 'gmail' | 'teams' | 'outlook';
|
||||||
dataTypes: string[];
|
dataTypes: string[];
|
||||||
directory?: string;
|
directory?: string;
|
||||||
onPromptUpdate?: (prompt: string) => void;
|
onPromptUpdate?: (prompt: string) => void;
|
||||||
|
onConfigUpdate?: (config: Record<string, unknown>) => void;
|
||||||
}) {
|
}) {
|
||||||
const startMutation = trpc.agent.journey.start.useMutation();
|
const startMutation = trpc.agent.journey.start.useMutation();
|
||||||
const messageMutation = trpc.agent.journey.message.useMutation();
|
const messageMutation = trpc.agent.journey.message.useMutation();
|
||||||
@@ -38,10 +40,14 @@ export function PromptBuilderChat({
|
|||||||
setSessionId(res.data.sessionId);
|
setSessionId(res.data.sessionId);
|
||||||
const msgs: { role: 'user' | 'assistant'; content: string }[] = [];
|
const msgs: { role: 'user' | 'assistant'; content: string }[] = [];
|
||||||
if (res.data.message.trim()) msgs.push({ role: 'assistant', content: res.data.message });
|
if (res.data.message.trim()) msgs.push({ role: 'assistant', content: res.data.message });
|
||||||
if (res.data.done && res.data.promptTemplate) {
|
if (res.data.done && res.data.agentConfig) {
|
||||||
onPromptUpdate?.(res.data.promptTemplate);
|
try {
|
||||||
|
const parsed = JSON.parse(res.data.agentConfig) as Record<string, unknown>;
|
||||||
|
onConfigUpdate?.(parsed);
|
||||||
|
} catch { /* ignore parse errors */ }
|
||||||
|
onPromptUpdate?.(res.data.agentConfig);
|
||||||
setIsDone(true);
|
setIsDone(true);
|
||||||
msgs.push({ role: 'assistant', content: 'Your extraction prompt has been saved.' });
|
msgs.push({ role: 'assistant', content: 'Your extraction config has been saved.' });
|
||||||
}
|
}
|
||||||
setMessages(msgs);
|
setMessages(msgs);
|
||||||
}
|
}
|
||||||
@@ -74,13 +80,17 @@ export function PromptBuilderChat({
|
|||||||
{
|
{
|
||||||
onSuccess: (res) => {
|
onSuccess: (res) => {
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
if (res.data.done && res.data.promptTemplate) {
|
if (res.data.done && res.data.agentConfig) {
|
||||||
onPromptUpdate?.(res.data.promptTemplate);
|
try {
|
||||||
|
const parsed = JSON.parse(res.data.agentConfig) as Record<string, unknown>;
|
||||||
|
onConfigUpdate?.(parsed);
|
||||||
|
} catch { /* ignore parse errors */ }
|
||||||
|
onPromptUpdate?.(res.data.agentConfig);
|
||||||
setIsDone(true);
|
setIsDone(true);
|
||||||
}
|
}
|
||||||
const toAdd: { role: 'user' | 'assistant'; content: string }[] = [];
|
const toAdd: { role: 'user' | 'assistant'; content: string }[] = [];
|
||||||
if (res.data.message.trim()) toAdd.push({ role: 'assistant', content: res.data.message });
|
if (res.data.message.trim()) toAdd.push({ role: 'assistant', content: res.data.message });
|
||||||
if (res.data.done) toAdd.push({ role: 'assistant', content: 'Your extraction prompt has been saved.' });
|
if (res.data.done) toAdd.push({ role: 'assistant', content: 'Your extraction config has been saved.' });
|
||||||
if (toAdd.length) setMessages(prev => [...prev, ...toAdd]);
|
if (toAdd.length) setMessages(prev => [...prev, ...toAdd]);
|
||||||
} else {
|
} else {
|
||||||
setMessages(prev => [...prev, { role: 'assistant', content: 'Something went wrong. Please try again.' }]);
|
setMessages(prev => [...prev, { role: 'assistant', content: 'Something went wrong. Please try again.' }]);
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ export interface LocalAgentConfig {
|
|||||||
name: string;
|
name: string;
|
||||||
directory: string;
|
directory: string;
|
||||||
dataTypes: string[];
|
dataTypes: string[];
|
||||||
promptTemplate: string;
|
/** Structured extraction config produced by the Journey setup flow. */
|
||||||
|
agentConfig: Record<string, unknown> | null;
|
||||||
scheduleCron: string;
|
scheduleCron: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
lastRunAt: number | null;
|
lastRunAt: number | null;
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ export const WsJourneyStartSchema = z.object({
|
|||||||
agentType: z.enum(['local_directory', 'gmail', 'teams', 'outlook']),
|
agentType: z.enum(['local_directory', 'gmail', 'teams', 'outlook']),
|
||||||
directory: z.string().optional(),
|
directory: z.string().optional(),
|
||||||
dataTypes: z.array(z.string()),
|
dataTypes: z.array(z.string()),
|
||||||
existingTemplate: z.string().nullable().optional(),
|
existingConfig: z.string().nullable().optional(),
|
||||||
});
|
});
|
||||||
export type WsJourneyStart = z.infer<typeof WsJourneyStartSchema>;
|
export type WsJourneyStart = z.infer<typeof WsJourneyStartSchema>;
|
||||||
|
|
||||||
@@ -205,7 +205,8 @@ export const WsJourneyReplySchema = z.object({
|
|||||||
sessionId: z.string(),
|
sessionId: z.string(),
|
||||||
message: z.string(),
|
message: z.string(),
|
||||||
done: z.boolean(),
|
done: z.boolean(),
|
||||||
promptTemplate: z.string().nullable().optional(),
|
/** Serialised AgentConfig JSON string produced by the journey when done=true. */
|
||||||
|
agentConfig: z.string().nullable().optional(),
|
||||||
});
|
});
|
||||||
export type WsJourneyReply = z.infer<typeof WsJourneyReplySchema>;
|
export type WsJourneyReply = z.infer<typeof WsJourneyReplySchema>;
|
||||||
|
|
||||||
@@ -299,7 +300,7 @@ export const LocalAgentConfigSchema = z.object({
|
|||||||
name: z.string(),
|
name: z.string(),
|
||||||
directoryPaths: z.array(z.string()),
|
directoryPaths: z.array(z.string()),
|
||||||
dataTypes: z.array(z.string()),
|
dataTypes: z.array(z.string()),
|
||||||
promptTemplate: z.string(),
|
agentConfig: z.record(z.string(), z.unknown()).nullable(),
|
||||||
scheduleCron: z.string(),
|
scheduleCron: z.string(),
|
||||||
enabled: z.boolean(),
|
enabled: z.boolean(),
|
||||||
lastRunAt: z.number().int().nullable().optional(),
|
lastRunAt: z.number().int().nullable().optional(),
|
||||||
@@ -345,7 +346,7 @@ export const JourneyMessageSchema = z.object({
|
|||||||
sessionId: z.string(),
|
sessionId: z.string(),
|
||||||
message: z.string(),
|
message: z.string(),
|
||||||
done: z.boolean(),
|
done: z.boolean(),
|
||||||
/** Present on the final message when `done === true`. */
|
/** Serialised AgentConfig JSON string — present on the final message when `done === true`. */
|
||||||
promptTemplate: z.string().optional(),
|
agentConfig: z.string().optional(),
|
||||||
});
|
});
|
||||||
export type JourneyMessage = z.infer<typeof JourneyMessageSchema>;
|
export type JourneyMessage = z.infer<typeof JourneyMessageSchema>;
|
||||||
|
|||||||
Reference in New Issue
Block a user