Python:DevUI Fixes (#1035)

* fix event reset on thread change, enable multiline input, enable pasting of files and screenshots

* UI updates and improved remove discovery

* ui and other fixes

---------

Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com>
This commit is contained in:
Victor Dibia
2025-09-30 17:21:22 -07:00
committed by GitHub
Unverified
parent 042099009f
commit fa9f5c1aed
41 changed files with 2949 additions and 814 deletions
@@ -5,6 +5,7 @@
import type {
AgentInfo,
AgentSource,
HealthResponse,
RunAgentRequest,
RunWorkflowRequest,
@@ -22,6 +23,8 @@ interface EntityInfo {
framework: string;
tools?: (string | Record<string, unknown>)[];
metadata: Record<string, unknown>;
source?: string;
original_url?: string;
executors?: string[];
workflow_dump?: Record<string, unknown>;
input_schema?: Record<string, unknown>;
@@ -52,16 +55,30 @@ interface ThreadApiObject {
created_at?: string;
}
const API_BASE_URL =
const DEFAULT_API_BASE_URL =
import.meta.env.VITE_API_BASE_URL !== undefined
? import.meta.env.VITE_API_BASE_URL
: "http://localhost:8080";
// Get backend URL from localStorage or default
function getBackendUrl(): string {
return localStorage.getItem("devui_backend_url") || DEFAULT_API_BASE_URL;
}
class ApiClient {
private baseUrl: string;
constructor(baseUrl: string = API_BASE_URL) {
this.baseUrl = baseUrl;
constructor(baseUrl?: string) {
this.baseUrl = baseUrl || getBackendUrl();
}
// Allow updating the base URL at runtime
setBaseUrl(url: string) {
this.baseUrl = url;
}
getBaseUrl(): string {
return this.baseUrl;
}
private async request<T>(
@@ -79,9 +96,17 @@ class ApiClient {
});
if (!response.ok) {
throw new Error(
`API request failed: ${response.status} ${response.statusText}`
);
// Try to extract error message from response body
let errorMessage = `API request failed: ${response.status} ${response.statusText}`;
try {
const errorData = await response.json();
if (errorData.detail) {
errorMessage = errorData.detail;
}
} catch {
// If parsing fails, use default message
}
throw new Error(errorMessage);
}
return response.json();
@@ -111,7 +136,7 @@ class ApiClient {
name: entity.name,
description: entity.description,
type: "agent",
source: "directory", // Default source
source: (entity.source as AgentSource) || "directory",
tools: (entity.tools || []).map((tool) =>
typeof tool === "string" ? tool : JSON.stringify(tool)
),
@@ -130,7 +155,7 @@ class ApiClient {
name: entity.name,
description: entity.description,
type: "workflow",
source: "directory",
source: (entity.source as AgentSource) || "directory",
executors: (entity.tools || []).map((tool) =>
typeof tool === "string" ? tool : JSON.stringify(tool)
),
@@ -440,6 +465,31 @@ class ApiClient {
body: JSON.stringify(request),
});
}
// Add entity from URL
async addEntity(url: string, metadata?: Record<string, unknown>): Promise<EntityInfo> {
const response = await this.request<{ success: boolean; entity: EntityInfo }>("/v1/entities/add", {
method: "POST",
body: JSON.stringify({ url, metadata }),
});
if (!response.success || !response.entity) {
throw new Error("Failed to add entity");
}
return response.entity;
}
// Remove entity by ID
async removeEntity(entityId: string): Promise<void> {
const response = await this.request<{ success: boolean }>(`/v1/entities/${entityId}`, {
method: "DELETE",
});
if (!response.success) {
throw new Error("Failed to remove entity");
}
}
}
// Export singleton instance