feat: slug change + streaming fix + specs
This commit is contained in:
parent
79541d133a
commit
13a1748162
@ -11,6 +11,8 @@ interface Props {
|
|||||||
|
|
||||||
export default function AgentModal({ agent, onClose, onUpdated }: Props) {
|
export default function AgentModal({ agent, onClose, onUpdated }: Props) {
|
||||||
const [name, setName] = useState(agent.name);
|
const [name, setName] = useState(agent.name);
|
||||||
|
const [slug, setSlug] = useState(agent.slug);
|
||||||
|
const [slugError, setSlugError] = useState("");
|
||||||
const [capabilities, setCapabilities] = useState(
|
const [capabilities, setCapabilities] = useState(
|
||||||
agent.agent_config?.capabilities?.join(", ") || ""
|
agent.agent_config?.capabilities?.join(", ") || ""
|
||||||
);
|
);
|
||||||
@ -34,8 +36,14 @@ export default function AgentModal({ agent, onClose, onUpdated }: Props) {
|
|||||||
setSaving(true);
|
setSaving(true);
|
||||||
try {
|
try {
|
||||||
const caps = capabilities.split(",").map((c) => c.trim()).filter(Boolean);
|
const caps = capabilities.split(",").map((c) => c.trim()).filter(Boolean);
|
||||||
|
const trimmedSlug = slug.trim().toLowerCase().replace(/[^a-z0-9_-]/g, '');
|
||||||
|
if (!trimmedSlug) {
|
||||||
|
setSlugError("Slug не может быть пустым");
|
||||||
|
return;
|
||||||
|
}
|
||||||
const updated = await updateMember(agent.id, {
|
const updated = await updateMember(agent.id, {
|
||||||
name: name.trim(),
|
name: name.trim(),
|
||||||
|
slug: trimmedSlug !== agent.slug ? trimmedSlug : undefined,
|
||||||
agent_config: {
|
agent_config: {
|
||||||
capabilities: caps,
|
capabilities: caps,
|
||||||
labels: agentLabels,
|
labels: agentLabels,
|
||||||
@ -47,8 +55,12 @@ export default function AgentModal({ agent, onClose, onUpdated }: Props) {
|
|||||||
});
|
});
|
||||||
onUpdated(updated);
|
onUpdated(updated);
|
||||||
onClose();
|
onClose();
|
||||||
} catch (e) {
|
} catch (e: any) {
|
||||||
console.error(e);
|
if (e?.response?.status === 409) {
|
||||||
|
setSlugError("Этот slug уже занят");
|
||||||
|
} else {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setSaving(false);
|
setSaving(false);
|
||||||
}
|
}
|
||||||
@ -114,6 +126,17 @@ export default function AgentModal({ agent, onClose, onUpdated }: Props) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm text-[var(--muted)] mb-1">Slug</label>
|
||||||
|
<input
|
||||||
|
value={slug}
|
||||||
|
onChange={(e) => { setSlug(e.target.value); setSlugError(""); }}
|
||||||
|
className={`w-full px-3 py-2 bg-[var(--bg)] border rounded-lg outline-none text-sm ${slugError ? 'border-red-500' : 'border-[var(--border)] focus:border-[var(--accent)]'}`}
|
||||||
|
placeholder="agent-slug"
|
||||||
|
/>
|
||||||
|
{slugError && <div className="text-xs text-red-400 mt-1">{slugError}</div>}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm text-[var(--muted)] mb-1">Capabilities</label>
|
<label className="block text-sm text-[var(--muted)] mb-1">Capabilities</label>
|
||||||
<input
|
<input
|
||||||
|
|||||||
@ -354,12 +354,12 @@ export default function ChatPanel({ chatId, projectId }: Props) {
|
|||||||
<span className="animate-pulse">●</span>
|
<span className="animate-pulse">●</span>
|
||||||
</div>
|
</div>
|
||||||
{streaming.thinking && (
|
{streaming.thinking && (
|
||||||
<details className="ml-5 mb-1" open>
|
<details className="ml-5 mb-1">
|
||||||
<summary className="text-xs text-[var(--muted)] cursor-pointer hover:text-[var(--fg)]">
|
<summary className="text-xs text-[var(--muted)] cursor-pointer hover:text-[var(--fg)]">
|
||||||
💭 Размышления
|
💭 Размышления ({streaming.thinking.length} символов)
|
||||||
</summary>
|
</summary>
|
||||||
<div className="mt-1 text-xs text-[var(--muted)] whitespace-pre-wrap bg-white/5 rounded p-2 max-h-[200px] overflow-y-auto">
|
<div className="mt-1 text-xs text-[var(--muted)] whitespace-pre-wrap bg-white/5 rounded p-2 max-h-[200px] overflow-y-auto">
|
||||||
{streaming.thinking}
|
{streaming.thinking.length > 500 ? '…' + streaming.thinking.slice(-500) : streaming.thinking}
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
)}
|
)}
|
||||||
@ -372,8 +372,8 @@ export default function ChatPanel({ chatId, projectId }: Props) {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{streaming.text && (
|
{streaming.text && !streaming.tools.some(t => t.status === "running") && (
|
||||||
<div className="ml-5 whitespace-pre-wrap">{streaming.text}</div>
|
<div className="ml-5 whitespace-pre-wrap">{streaming.text.length > 500 ? '…' + streaming.text.slice(-500) : streaming.text}</div>
|
||||||
)}
|
)}
|
||||||
{!streaming.text && streaming.tools.length === 0 && !streaming.thinking && (
|
{!streaming.text && streaming.tools.length === 0 && !streaming.thinking && (
|
||||||
<div className="ml-5 text-[var(--muted)] italic">Думает...</div>
|
<div className="ml-5 text-[var(--muted)] italic">Думает...</div>
|
||||||
|
|||||||
@ -316,6 +316,7 @@ export async function createMember(data: {
|
|||||||
|
|
||||||
export async function updateMember(memberId: string, data: {
|
export async function updateMember(memberId: string, data: {
|
||||||
name?: string;
|
name?: string;
|
||||||
|
slug?: string;
|
||||||
role?: string;
|
role?: string;
|
||||||
status?: string;
|
status?: string;
|
||||||
agent_config?: Partial<AgentConfig>;
|
agent_config?: Partial<AgentConfig>;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user