Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -188,3 +188,6 @@ agentex/docs/site/
# tarballs for building
*.tgz
**/*.tar.gz

# Node
node_modules/
2 changes: 1 addition & 1 deletion agentex-ui/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ RUN npm config set maxsockets 3

# Install dependencies with better reliability settings
RUN npm config set registry https://registry.npmjs.org/ && \
npm ci --omit=dev --verbose
npm ci --verbose

# Copy source code (node_modules and .next excluded by .dockerignore)
COPY . .
Expand Down
75 changes: 68 additions & 7 deletions agentex-ui/components/primary-content/prompt-input.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
'use client';

import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';

import { closeBrackets } from '@codemirror/autocomplete';
import { json } from '@codemirror/lang-json';
import { Prec } from '@codemirror/state';
import { EditorView, keymap } from '@codemirror/view';
import CodeMirror from '@uiw/react-codemirror';
import { DataContent, TextContent } from 'agentex/resources';
import { ArrowUp } from 'lucide-react';
import { ArrowUp, Paperclip, X } from 'lucide-react';

import { useAgentexClient } from '@/components/providers';
import { IconButton } from '@/components/ui/icon-button';
Expand All @@ -20,6 +26,7 @@ import {
useSafeSearchParams,
} from '@/hooks/use-safe-search-params';
import { useSendMessage } from '@/hooks/use-task-messages';
import { useUploadFile, FileAttachment } from '@/hooks/use-upload-file';

type PromptInputProps = {
prompt: string;
Expand Down Expand Up @@ -47,6 +54,9 @@ export function PromptInput({ prompt, setPrompt }: PromptInputProps) {
const { taskID, agentName, updateParams } = useSafeSearchParams();
const [isClient, setIsClient] = useState(false);
const [isSendingJSON, setIsSendingJSON] = useState(false);
const [attachments, setAttachments] = useState<FileAttachment[]>([]);
const { uploadFile, isUploading } = useUploadFile();
const fileInputRef = useRef<HTMLInputElement>(null);

const { agentexClient } = useAgentexClient();

Expand Down Expand Up @@ -132,13 +142,13 @@ export function PromptInput({ prompt, setPrompt }: PromptInputProps) {
type: 'text',
author: 'user',
format: 'plain',
attachments: [],
attachments: attachments,
content: prompt as string,
};

await sendMessageMutation.mutateAsync({
taskId: currentTaskId,
agentName: agentName!,
taskId: currentTaskId ?? '',
agentName: agentName ?? '',
content,
});
}, [
Expand All @@ -151,13 +161,25 @@ export function PromptInput({ prompt, setPrompt }: PromptInputProps) {
sendMessageMutation,
setPrompt,
isSendingJSON,
attachments,
]);

return (
<div className="flex w-full max-w-3xl flex-col gap-2">
<div
className={`border-input dark:bg-input ${isDisabled ? 'bg-muted scale-90 cursor-not-allowed' : 'scale-100'} flex w-full items-center justify-between rounded-4xl border py-2 pr-2 pl-6 shadow-sm transition-transform duration-300 disabled:cursor-not-allowed`}
>
<div className="flex items-center">
{!isSendingJSON && (
<IconButton
className="text-muted-foreground hover:text-foreground mr-2"
onClick={() => fileInputRef.current?.click()}
disabled={isDisabled || isUploading}
icon={Paperclip}
aria-label="Attach File"
/>
)}
</div>
{isSendingJSON ? (
<DataInput
prompt={prompt}
Expand All @@ -178,11 +200,50 @@ export function PromptInput({ prompt, setPrompt }: PromptInputProps) {
<IconButton
className="pointer-events-auto size-10 rounded-full"
onClick={handleSendPrompt}
disabled={isDisabled || !prompt.trim()}
disabled={isDisabled || (!prompt.trim() && attachments.length === 0)}
icon={ArrowUp}
aria-label="Send Prompt"
/>
<input
type="file"
ref={fileInputRef}
className="hidden"
onChange={async e => {
const file = e.target.files?.[0];
if (file) {
const attachment = await uploadFile(file);
if (attachment) {
setAttachments([...attachments, attachment]);
}
// Reset input so same file can be selected again if needed
e.target.value = '';
}
}}
/>
</div>

{/* Attachments Display */}
{attachments.length > 0 && (
<div className="flex flex-wrap gap-2 px-1">
{attachments.map((file, idx) => (
<div
key={file.file_id}
className="bg-muted flex items-center gap-1 rounded-md px-2 py-1 text-xs"
>
<span className="max-w-[150px] truncate">{file.name}</span>
<button
onClick={() =>
setAttachments(prev => prev.filter((_, i) => i !== idx))
}
className="hover:bg-background/20 rounded-full p-0.5"
>
<X size={12} />
</button>
</div>
))}
</div>
)}

<div
className="text-muted-foreground ml-4 flex items-center gap-2 rounded-full text-sm"
style={{
Expand Down Expand Up @@ -278,7 +339,7 @@ const DataInput = ({
className="dark:bg-input/30 mx-1 w-full rounded-full text-sm"
value={prompt}
onChange={(value: string) => setPrompt(value)}
onCreateEditor={view => {
onCreateEditor={(view: EditorView) => {
codeMirrorViewRef.current = view;
}}
extensions={[json(), noOutlineTheme, closeBrackets(), commandEnterKeymap]}
Expand Down
3 changes: 1 addition & 2 deletions agentex-ui/components/providers/agentex-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
'use client';

import { createContext, useContext, useMemo, type ReactNode } from 'react';

import AgentexSDK from 'agentex';

interface AgentexContextValue {
agentexClient: AgentexSDK;
agentexClient: any;
sgpAppURL: string;
}

Expand Down
9 changes: 4 additions & 5 deletions agentex-ui/hooks/custom-subscribe-task-state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { aggregateMessageEvents } from 'agentex/lib/aggregate-message-events';
import { compareDateStrings } from 'agentex/lib/compare-date-strings';
import { taskStreamEventGenerator } from 'agentex/lib/task-stream-event-generator';

import type { Agentex } from 'agentex';
import type { Agent, Task, TaskMessage } from 'agentex/resources';

/**
Expand Down Expand Up @@ -84,7 +83,7 @@ type ErrorReturn = {
* This promise will never reject.
*/
export async function subscribeTaskState(
client: Agentex,
client: any,
taskIdentifier: TaskIdentifier,
eventListener: ITaskEventListener,
options?: {
Expand Down Expand Up @@ -131,19 +130,19 @@ export async function subscribeTaskState(

// pause reading from stream until we initialize state
[, , messages] = await Promise.all([
client.tasks.retrieve(taskID, null, { signal }).then(res => {
client.tasks.retrieve(taskID, null, { signal }).then((res: any) => {
eventListener.onTaskChange(res);
return res;
}),
client.agents
.list({ task_id: taskID }, { signal })
.then(res => {
.then((res: any) => {
eventListener.onAgentsChange(res);
return res;
}),
client.messages
.list({ task_id: taskID }, { signal })
.then(res => {
.then((res: any) => {
const chronologicalMessages = res.slice().reverse();
eventListener.onMessagesChange(chronologicalMessages);
return res;
Expand Down
5 changes: 2 additions & 3 deletions agentex-ui/hooks/use-agents.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useQuery } from '@tanstack/react-query';

import type AgentexSDK from 'agentex';
import type { Agent } from 'agentex/resources';

export const agentsKeys = {
Expand All @@ -13,10 +12,10 @@ export const agentsKeys = {
* This hook retrieves all agent definitions that can execute tasks. Refetch on window focus
* is disabled to prevent unnecessary API calls when switching browser tabs.
*
* @param agentexClient - AgentexSDK - The SDK client used to communicate with the Agentex API
* @param agentexClient - any - The SDK client used to communicate with the Agentex API
* @returns UseQueryResult<Agent[]> - React Query result containing the array of agent definitions
*/
export function useAgents(agentexClient: AgentexSDK) {
export function useAgents(agentexClient: any) {
return useQuery({
queryKey: agentsKeys.all,
queryFn: async (): Promise<Agent[]> => {
Expand Down
17 changes: 8 additions & 9 deletions agentex-ui/hooks/use-create-task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { agentRPCNonStreaming } from 'agentex/lib';
import { toast } from '@/components/ui/toast';
import { tasksKeys } from '@/hooks/use-tasks';

import type AgentexSDK from 'agentex';
import type {
Agent,
Task,
Expand All @@ -36,10 +35,10 @@ export function updateTaskInInfiniteQuery(
): InfiniteData<TaskListResponse> | undefined {
if (!data) return undefined;

if (data.pages.some(page => page.some(t => t.id === task.id))) {
if (data.pages.some((page: any) => page.some((t: any) => t.id === task.id))) {
return {
pages: data.pages.map(page =>
page.map(t =>
pages: data.pages.map((page: any) =>
page.map((t: any) =>
t.id === task.id ? { ...task, agents: t.agents || null } : t
)
),
Expand Down Expand Up @@ -93,7 +92,7 @@ type CreateTaskParams = {
export function useCreateTask({
agentexClient,
}: {
agentexClient: AgentexSDK;
agentexClient: any;
}) {
const queryClient = useQueryClient();

Expand All @@ -117,21 +116,21 @@ export function useCreateTask({

return response.result;
},
onSuccess: (task, variables) => {
onSuccess: (task: any, variables: any) => {
queryClient.setQueryData<TaskRetrieveResponse>(
tasksKeys.individualById(task.id),
task
);
queryClient.setQueryData<InfiniteData<TaskListResponse>>(
tasksKeys.all,
data => updateTaskInInfiniteQuery(task, variables.agentName, data)
(data: any) => updateTaskInInfiniteQuery(task, variables.agentName, data)
);
queryClient.setQueryData<InfiniteData<TaskListResponse>>(
tasksKeys.byAgentName(variables.agentName),
data => updateTaskInInfiniteQuery(task, variables.agentName, data)
(data: any) => updateTaskInInfiniteQuery(task, variables.agentName, data)
);
},
onError: error => {
onError: (error: any) => {
toast.error({
title: 'Failed to create task',
message: error instanceof Error ? error.message : 'Please try again.',
Expand Down
5 changes: 2 additions & 3 deletions agentex-ui/hooks/use-task-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { v4 } from 'uuid';
import { toast } from '@/components/ui/toast';
import { agentsKeys } from '@/hooks/use-agents';

import type AgentexSDK from 'agentex';
import type { IDeltaAccumulator } from 'agentex/lib';
import type { Agent, TaskMessage, TaskMessageContent } from 'agentex/resources';

Expand Down Expand Up @@ -39,7 +38,7 @@ export function useTaskMessages({
agentexClient,
taskId,
}: {
agentexClient: AgentexSDK;
agentexClient: any;
taskId: string;
}) {
return useQuery({
Expand Down Expand Up @@ -76,7 +75,7 @@ type SendMessageParams = {
export function useSendMessage({
agentexClient,
}: {
agentexClient: AgentexSDK;
agentexClient: any;
}) {
const queryClient = useQueryClient();

Expand Down
3 changes: 1 addition & 2 deletions agentex-ui/hooks/use-task-subscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { taskMessagesKeys } from '@/hooks/use-task-messages';
import type { TaskMessagesData } from '@/hooks/use-task-messages';
import { tasksKeys } from '@/hooks/use-tasks';

import type AgentexSDK from 'agentex';
import type { TaskListResponse, TaskRetrieveResponse } from 'agentex/resources';

/**
Expand All @@ -31,7 +30,7 @@ export function useTaskSubscription({
agentName,
enabled = true,
}: {
agentexClient: AgentexSDK;
agentexClient: any;
taskId: string;
agentName: string;
enabled?: boolean;
Expand Down
7 changes: 3 additions & 4 deletions agentex-ui/hooks/use-tasks.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useInfiniteQuery, useQuery } from '@tanstack/react-query';

import type AgentexSDK from 'agentex';
import type {
TaskListResponse,
TaskListParams,
Expand Down Expand Up @@ -31,7 +30,7 @@ export function useTask({
agentexClient,
taskId,
}: {
agentexClient: AgentexSDK;
agentexClient: any;
taskId: string;
}) {
return useQuery({
Expand All @@ -57,7 +56,7 @@ export function useTask({
* @returns UseInfiniteQueryResult<InfiniteData<TaskListResponse>> - Infinite query with fetchNextPage support
*/
export function useInfiniteTasks(
agentexClient: AgentexSDK,
agentexClient: any,
options?: { agentName?: string; limit?: number }
) {
const { agentName, limit = 30 } = options || {};
Expand All @@ -73,7 +72,7 @@ export function useInfiniteTasks(
};
return agentexClient.tasks.list(params);
},
getNextPageParam: (lastPage, allPages) => {
getNextPageParam: (lastPage: any, allPages: any) => {
if (lastPage.length < limit) {
return undefined;
}
Expand Down
Loading