diff --git a/nginx/nginx.tmpl b/nginx/nginx.tmpl
index e359f2da9..2f7487736 100644
--- a/nginx/nginx.tmpl
+++ b/nginx/nginx.tmpl
@@ -35,6 +35,22 @@ http {
location /v1/healthz {
return 200 'OK';
}
+ location ~ ^/teams/([^/]+)/agents/([^/]+)/(.*)$ {
+ set $team_id $1;
+ set $agent_name $2;
+ set $agent_path $3;
+ resolver ${KUBE_DNS_SERVICE_HOST} valid=10s;
+ proxy_pass http://$agent_name.team-$team_id.svc.cluster.local:9099/$agent_path$is_args$args;
+ proxy_http_version 1.1;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ # Support for streaming responses
+ proxy_buffering off;
+ proxy_cache off;
+ chunked_transfer_encoding on;
+ }
location ~* \.(?:css|js|eot|woff|woff2|ttf|svg|otf) {
# Enable GZip for static files
gzip_static on;
diff --git a/nginx/run.sh b/nginx/run.sh
index 538084e37..e802c172c 100755
--- a/nginx/run.sh
+++ b/nginx/run.sh
@@ -4,6 +4,10 @@ CONTEXT_PATH=${CONTEXT_PATH:-''}
find build -type f -print0 | xargs -0 sed -i -e "s/##CONTEXT_PATH##/$CONTEXT_PATH/g"
-envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" /etc/nginx/nginx.conf
+# Extract DNS nameserver for nginx resolver (fallback to resolv.conf if env var not set)
+DNS_SERVER=${KUBE_DNS_SERVICE_HOST:-$(awk '/^nameserver/{print $2; exit}' /etc/resolv.conf)}
+
+# Generate nginx.conf with DNS resolver substituted
+sed "s/\${KUBE_DNS_SERVICE_HOST}/$DNS_SERVER/g" /app/nginx.tmpl > /etc/nginx/nginx.conf
exec nginx -g 'daemon off;'
diff --git a/public/assets/agents_icon.svg b/public/assets/agents_icon.svg
new file mode 100644
index 000000000..33621541e
--- /dev/null
+++ b/public/assets/agents_icon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/i18n/en/common.json b/public/i18n/en/common.json
index eb6b64476..47b616a97 100644
--- a/public/i18n/en/common.json
+++ b/public/i18n/en/common.json
@@ -99,6 +99,10 @@
"Knowledge-base_plural": "Knowledge Bases",
"TITLE_KNOWLEDGE_BASE": "Knowledge base details",
"TITLE_KNOWLEDGE_BASES": "Knowledge bases - {{scope}}",
+ "Agent": "Agent",
+ "Agent_plural": "Agents",
+ "TITLE_AGENT": "Agent details",
+ "TITLE_AGENTS": "Agents - {{scope}}",
"TITLE_NETWORK_POLICY": "Network policy details",
"TITLE_NETWORK_POLICIES": "Network policies - {{scope}}"
}
diff --git a/src/App.tsx b/src/App.tsx
index c3c96b903..e0e55f6d6 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -40,6 +40,8 @@ import CodeRepositoriesCreateEditPage from 'pages/code-repositories/create-edit/
import CodeRepositoriesOverviewPage from 'pages/code-repositories/overview/CodeRepositoriesOverviewPage'
import KnowledgeBasesCreateEditPage from 'pages/knowledge-bases/create-edit/KnowledgeBasesCreateEditPage'
import KnowledgeBasesOverviewPage from 'pages/knowledge-bases/overview/KnowledgeBasesOverviewPage'
+import AgentsCreateEditPage from 'pages/agents/create-edit/AgentsCreateEditPage'
+import AgentsOverviewPage from 'pages/agents/overview/AgentsOverviewPage'
import NetworkPoliciesOverviewPage from 'pages/network-policies/overview/NetworkPoliciesOverviewPage'
import NetworkPoliciesIngressCreateEditPage from 'pages/network-policies/create-edit/NetworkPoliciesIngressCreateEditPage'
import NetworkPoliciesEgressCreateEditPage from 'pages/network-policies/create-edit/NetworkPoliciesEgressCreateEditPage'
@@ -124,6 +126,18 @@ function App() {
exact
/>
+
+
+
+
diff --git a/src/components/AgentPlayground.tsx b/src/components/AgentPlayground.tsx
new file mode 100644
index 000000000..a0895c9aa
--- /dev/null
+++ b/src/components/AgentPlayground.tsx
@@ -0,0 +1,331 @@
+import React, { useEffect, useRef, useState } from 'react'
+import { Alert, Box, IconButton, TextField, Typography, keyframes } from '@mui/material'
+import SendIcon from '@mui/icons-material/Send'
+import DeleteIcon from '@mui/icons-material/Delete'
+import StopIcon from '@mui/icons-material/StopCircle'
+import Markdown from './Markdown'
+import { Paper } from './Paper'
+import Iconify from './Iconify'
+
+const thinkingAnimation = keyframes`
+ 0%, 60%, 100% {
+ opacity: 0.3;
+ }
+ 30% {
+ opacity: 1;
+ }
+`
+
+interface Message {
+ role: 'user' | 'assistant'
+ content: string
+ id: string
+}
+
+interface AgentPlaygroundProps {
+ teamId: string
+ agentName: string
+}
+
+export function AgentPlayground({ teamId, agentName }: AgentPlaygroundProps): React.ReactElement {
+ const [messages, setMessages] = useState([])
+ const [input, setInput] = useState('')
+ const [loading, setLoading] = useState(false)
+ const [error, setError] = useState(null)
+ const messagesEndRef = useRef(null)
+ const messagesContainerRef = useRef(null)
+ const abortControllerRef = useRef(null)
+
+ const scrollToBottom = () => {
+ if (messagesContainerRef.current) messagesContainerRef.current.scrollTop = messagesContainerRef.current.scrollHeight
+ }
+
+ useEffect(() => {
+ scrollToBottom()
+ }, [messages])
+
+ const handleStop = () => {
+ if (abortControllerRef.current) {
+ abortControllerRef.current.abort()
+ abortControllerRef.current = null
+ setLoading(false)
+ // Remove the empty assistant message if it exists
+ setMessages((prev) => {
+ const lastMessage = prev[prev.length - 1]
+ if (lastMessage?.role === 'assistant' && !lastMessage.content) return prev.slice(0, -1)
+
+ return prev
+ })
+ }
+ }
+
+ const handleSend = async () => {
+ if (!input.trim() || loading) return
+
+ const userMessage: Message = { role: 'user', content: input.trim(), id: `user-${Date.now()}` }
+ const assistantId = `assistant-${Date.now()}`
+ const newMessages = [...messages, userMessage]
+
+ // Immediately add user message and empty assistant message for thinking animation
+ setMessages([...newMessages, { role: 'assistant', content: '', id: assistantId }])
+ setInput('')
+ setLoading(true)
+ setError(null)
+
+ // Create new abort controller for this request
+ abortControllerRef.current = new AbortController()
+
+ try {
+ // Call agent service through nginx proxy to handle http and mixed-content issues
+ // In development: use /agent proxy (port-forward to localhost:9099)
+ // In cluster: use /teams/{teamId}/agents/{agentName} proxy (nginx routes to internal service)
+ const isDev = process.env.NODE_ENV === 'development'
+ const agentServiceUrl = isDev
+ ? `/agent/v1/chat/completions`
+ : `/teams/${teamId}/agents/${agentName}/v1/chat/completions`
+
+ const requestBody = {
+ messages: newMessages.map((msg) => ({ role: msg.role, content: msg.content })),
+ stream: true,
+ model: 'rag-pipeline',
+ }
+
+ const response = await fetch(agentServiceUrl, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(requestBody),
+ signal: abortControllerRef.current.signal,
+ })
+
+ if (!response.ok) {
+ const errorData = await response.json().catch(() => ({ error: { message: 'Unknown error' } }))
+ const errorMessage = errorData?.error?.message || `Error: ${response.status}`
+ throw new Error(typeof errorMessage === 'string' ? errorMessage : 'Unknown error')
+ }
+
+ // Handle streaming response
+ const reader = response.body?.getReader()
+ const decoder = new TextDecoder()
+
+ if (reader) {
+ // Process streaming response
+ const processStream = async () => {
+ let accumulatedContent = ''
+ // eslint-disable-next-line no-constant-condition
+ while (true) {
+ // eslint-disable-next-line no-await-in-loop
+ const { done, value } = await reader.read()
+ if (done) break
+
+ const chunk = decoder.decode(value, { stream: true })
+ const lines = chunk.split('\n')
+
+ // eslint-disable-next-line no-loop-func
+ lines.forEach((line) => {
+ if (line.startsWith('data: ')) {
+ const data = line.slice(6)
+ if (data === '[DONE]') return
+
+ try {
+ const parsed = JSON.parse(data)
+ const content = parsed.choices?.[0]?.delta?.content || ''
+ if (content) {
+ accumulatedContent += content
+ const currentContent = accumulatedContent
+ setMessages((prev) => {
+ const updated = [...prev]
+ const lastIndex = updated.length - 1
+ if (lastIndex >= 0 && updated[lastIndex].id === assistantId)
+ updated[lastIndex] = { ...updated[lastIndex], content: currentContent }
+ return updated
+ })
+ }
+ } catch {
+ // Ignore JSON parse errors for malformed chunks
+ }
+ }
+ })
+ }
+ }
+
+ await processStream()
+ } else {
+ // Non-streaming response
+ const data = await response.json()
+ const assistantContent = data.choices?.[0]?.message?.content || 'No response'
+ setMessages([...newMessages, { role: 'assistant', content: assistantContent, id: assistantId }])
+ }
+ } catch (err) {
+ // Don't show error if request was aborted by user
+ if (err instanceof Error && err.name === 'AbortError') return
+
+ const errorMessage = err instanceof Error ? err.message : 'Failed to send message'
+ setError(errorMessage)
+ } finally {
+ setLoading(false)
+ abortControllerRef.current = null
+ }
+ }
+
+ const handleClear = () => {
+ setMessages([])
+ setError(null)
+ }
+
+ const handleKeyDown = (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault()
+ handleSend()
+ }
+ }
+
+ return (
+
+
+ {/* Header */}
+
+ Agent Playground
+
+
+
+
+
+ {/* Messages Area */}
+
+ {messages.length === 0 ? (
+
+
+
+ Ask your agent a question to start evaluating.
+
+
+ ) : (
+ messages.map((message) => (
+
+
+
+ {message.role === 'user' ? 'You' : 'Agent'}
+
+ {(() => {
+ // Show thinking animation for empty assistant message while loading
+ if (message.role === 'assistant' && !message.content && loading) {
+ return (
+
+
+
+
+
+ )
+ }
+
+ // Render assistant message with markdown
+ if (message.role === 'assistant') {
+ return (
+
+ )
+ }
+
+ return (
+
+ {message.content}
+
+ )
+ })()}
+
+
+ ))
+ )}
+
+
+
+ {/* Error Display */}
+ {error && (
+ setError(null)}>
+ {error}
+
+ )}
+
+ {/* Input Area */}
+
+ setInput(e.target.value)}
+ onKeyDown={handleKeyDown}
+ placeholder='Type your message...'
+ disabled={loading}
+ variant='outlined'
+ size='small'
+ />
+ {loading ? (
+
+
+
+ ) : (
+
+
+
+ )}
+
+
+
+ )
+}
diff --git a/src/components/Markdown.tsx b/src/components/Markdown.tsx
index 0e651c5c1..345dcc4aa 100644
--- a/src/components/Markdown.tsx
+++ b/src/components/Markdown.tsx
@@ -1,4 +1,7 @@
-import { Card, styled } from '@mui/material'
+import React, { useState } from 'react'
+import { Box, Card, IconButton, Tooltip, styled } from '@mui/material'
+import ContentCopyIcon from '@mui/icons-material/ContentCopy'
+import CheckIcon from '@mui/icons-material/Check'
import MarkdownJSX from 'markdown-to-jsx'
// Higher-order component to generate markdown components
@@ -30,15 +33,57 @@ const Code = createMDComp('code', {
backgroundColor: '#6e768164',
textWrap: 'pretty',
})
-const Pre = createMDComp('pre', {
- ...mb,
- backgroundColor: '#6e768164',
- padding: '12px',
- borderRadius: '6px',
- '& > code': {
- backgroundColor: 'transparent',
- },
-})
+
+// Code block with copy button
+function CodeBlock({ children }: any) {
+ const [copied, setCopied] = useState(false)
+
+ const handleCopy = () => {
+ const codeText = children?.props?.children || children
+ const textToCopy = typeof codeText === 'string' ? codeText : String(codeText)
+ navigator.clipboard.writeText(textToCopy.trim())
+ setCopied(true)
+ setTimeout(() => setCopied(false), 2000)
+ }
+
+ return (
+
+ code': {
+ backgroundColor: 'transparent',
+ },
+ }}
+ >
+ {children}
+
+
+
+ {copied ? : }
+
+
+
+ )
+}
const Ol = createMDComp('ol', { ...mb, paddingLeft: '32px' })
const Li = createMDComp('li', { ...lh })
const Table = createMDComp('table', { ...mb, borderCollapse: 'collapse' })
@@ -77,7 +122,7 @@ export default function Markdown({ readme, sx }: Props) {
component: Code,
},
pre: {
- component: Pre,
+ component: CodeBlock,
},
li: {
component: Li,
diff --git a/src/components/NavConfig.tsx b/src/components/NavConfig.tsx
index 60a50a843..fba7b25f8 100644
--- a/src/components/NavConfig.tsx
+++ b/src/components/NavConfig.tsx
@@ -52,6 +52,12 @@ export default function NavConfig() {
path: `/catalogs/${oboTeamId}`,
icon: getIcon('developer_guide_icon.svg'),
},
+ {
+ title: 'Agents',
+ path: `/teams/${oboTeamId}/agents`,
+ icon: getIcon('agents_icon.svg'),
+ hidden: !aiEnabled,
+ },
{
title: 'Knowledge Bases',
path: `/teams/${oboTeamId}/knowledge-bases`,
diff --git a/src/components/Setting.tsx b/src/components/Setting.tsx
index 1c3f158d2..ed5e4f907 100644
--- a/src/components/Setting.tsx
+++ b/src/components/Setting.tsx
@@ -67,6 +67,7 @@ export const getSettingUiSchema = (settings: GetSettingsInfoApiResponse, setting
isPreInstalled: { 'ui:widget': 'hidden' },
adminPassword: { 'ui:widget': 'hidden' },
useORCS: { 'ui:widget': 'hidden' },
+ aiEnabled: { 'ui:widget': 'hidden' },
},
kms: {
sops: {
diff --git a/src/components/forms/TextArea.tsx b/src/components/forms/TextArea.tsx
index 7d6e41e2b..854cadbf3 100644
--- a/src/components/forms/TextArea.tsx
+++ b/src/components/forms/TextArea.tsx
@@ -168,6 +168,11 @@ export function AutoResizableTextarea({
adjustSize()
}, [rest.value])
+ // Sync internal value state with external value prop
+ useEffect(() => {
+ if (!showLock && rest.value !== value) setValue(rest.value)
+ }, [rest.value, showLock])
+
return (
): React.ReactElement {
+ const { t } = useTranslation()
+
+ const [create, { isLoading: isLoadingCreate, isSuccess: isSuccessCreate }] = useCreateAplAgentMutation()
+ const [update, { isLoading: isLoadingUpdate, isSuccess: isSuccessUpdate }] = useEditAplAgentMutation()
+ const [del, { isLoading: isLoadingDelete, isSuccess: isSuccessDelete }] = useDeleteAplAgentMutation()
+ const { data, isLoading, isFetching, isError, refetch } = useGetAplAgentQuery(
+ { teamId, agentName },
+ { skip: !agentName },
+ )
+ const { data: aiModels } = useGetAiModelsQuery()
+ const { data: knowledgeBases } = useGetAplKnowledgeBasesQuery({ teamId })
+
+ const isDirty = useAppSelector(({ global: { isDirty } }) => isDirty)
+ useEffect(() => {
+ if (isDirty !== false) return
+ if (!isFetching) refetch()
+ }, [isDirty])
+
+ type FormType = CreateAplAgentApiArg['body']
+
+ const defaultValues: FormType = {
+ kind: 'AkamaiAgent' as const,
+ metadata: {
+ name: '',
+ },
+ spec: {
+ foundationModel: '',
+ agentInstructions: '',
+ tools: [],
+ },
+ }
+
+ const methods = useForm({
+ resolver: yupResolver(agentSchema) as unknown as Resolver,
+ defaultValues,
+ })
+
+ const {
+ register,
+ reset,
+ handleSubmit,
+ watch,
+ formState: { errors },
+ setValue,
+ } = methods
+
+ useEffect(() => {
+ if (data) reset(data)
+ }, [data, reset])
+
+ const onSubmit = (formData: FormType) => {
+ const body = { ...formData }
+
+ if (agentName) update({ teamId, agentName, body })
+ else create({ teamId, body })
+ }
+
+ const mutating = isLoadingCreate || isLoadingUpdate || isLoadingDelete
+ if (!mutating && (isSuccessCreate || isSuccessUpdate || isSuccessDelete))
+ return
+
+ const loading = isLoading
+
+ if (loading) return
+
+ return (
+
+
+
+
+
+
+ {agentName && }
+
+
+ )
+}
diff --git a/src/pages/agents/create-edit/create-edit-agents.validator.ts b/src/pages/agents/create-edit/create-edit-agents.validator.ts
new file mode 100644
index 000000000..44a6c15f8
--- /dev/null
+++ b/src/pages/agents/create-edit/create-edit-agents.validator.ts
@@ -0,0 +1,21 @@
+import * as yup from 'yup'
+
+// Schema for Agent form validation
+export const agentSchema = yup.object({
+ kind: yup.string().oneOf(['AkamaiAgent']).required(),
+ metadata: yup.object({
+ name: yup
+ .string()
+ .required('Agent name is required')
+ .min(2, 'Agent name must be at least 2 characters')
+ .matches(
+ /^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/,
+ 'Name must start and end with a lowercase letter or number, and can only contain lowercase letters, numbers, and hyphens',
+ ),
+ }),
+ spec: yup.object({
+ foundationModel: yup.string().required('Please select a foundation model'),
+ knowledgeBase: yup.string().optional(),
+ agentInstructions: yup.string().required('Please enter agent instructions'),
+ }),
+})
diff --git a/src/pages/agents/index.tsx b/src/pages/agents/index.tsx
new file mode 100644
index 000000000..7e029d6bd
--- /dev/null
+++ b/src/pages/agents/index.tsx
@@ -0,0 +1,7 @@
+import AgentsOverviewPage from './overview/AgentsOverviewPage'
+import AgentsCreateEditPage from './create-edit/AgentsCreateEditPage'
+
+export default {
+ AgentsOverviewPage,
+ AgentsCreateEditPage,
+}
diff --git a/src/pages/agents/overview/AgentsOverviewPage.tsx b/src/pages/agents/overview/AgentsOverviewPage.tsx
new file mode 100644
index 000000000..915cc62aa
--- /dev/null
+++ b/src/pages/agents/overview/AgentsOverviewPage.tsx
@@ -0,0 +1,93 @@
+import PaperLayout from 'layouts/Paper'
+import React, { useEffect } from 'react'
+import { useTranslation } from 'react-i18next'
+import { RouteComponentProps } from 'react-router-dom'
+import { getRole } from 'utils/data'
+import { useGetAplAgentsQuery } from 'redux/otomiApi'
+import { useAppSelector } from 'redux/hooks'
+import { HeadCell } from '../../../components/EnhancedTable'
+import RLink from '../../../components/Link'
+import ListTable from '../../../components/ListTable'
+
+const getAgentName = (): CallableFunction =>
+ function (row: any): string | React.ReactElement {
+ const { teamId, name }: { teamId: string; name: string } = row
+ const path = `/teams/${teamId}/agents/${encodeURIComponent(name)}`
+ return (
+
+ {name}
+
+ )
+ }
+
+const getStatus = (): CallableFunction =>
+ function (row: any): string {
+ const { status } = row
+ return status?.phase || 'Unknown'
+ }
+
+const getFoundationModel = (): CallableFunction =>
+ function (row: any): string {
+ const { foundationModel } = row
+ return foundationModel || 'N/A'
+ }
+
+interface Params {
+ teamId: string
+}
+
+export default function AgentsOverviewPage({
+ match: {
+ params: { teamId },
+ },
+}: RouteComponentProps): React.ReactElement {
+ const { t } = useTranslation()
+
+ const { data: agents, isLoading, isFetching, refetch } = useGetAplAgentsQuery({ teamId })
+
+ const isDirty = useAppSelector(({ global: { isDirty } }) => isDirty)
+ useEffect(() => {
+ if (isDirty !== false) return
+ if (!isFetching) refetch()
+ }, [isDirty])
+
+ // Transform API response to match table format
+ const transformedData =
+ agents?.map((agent) => ({
+ name: agent.metadata.name,
+ teamId,
+ status: agent.status,
+ foundationModel: agent.spec.foundationModel,
+ })) || []
+
+ const headCells: HeadCell[] = [
+ {
+ id: 'name',
+ label: t('Name'),
+ renderer: getAgentName(),
+ },
+ {
+ id: 'status',
+ label: t('Status'),
+ renderer: getStatus(),
+ },
+ {
+ id: 'foundationModel',
+ label: t('Foundation Model'),
+ renderer: getFoundationModel(),
+ },
+ ]
+
+ const customButtonText = () => Create Agent
+
+ const comp = (
+
+ )
+ return
+}
diff --git a/src/pages/knowledge-bases/overview/KnowledgeBasesOverviewPage.tsx b/src/pages/knowledge-bases/overview/KnowledgeBasesOverviewPage.tsx
index a642a40cd..9ec4e3d45 100644
--- a/src/pages/knowledge-bases/overview/KnowledgeBasesOverviewPage.tsx
+++ b/src/pages/knowledge-bases/overview/KnowledgeBasesOverviewPage.tsx
@@ -26,12 +26,6 @@ const getStatus = (): CallableFunction =>
return status?.phase || 'Unknown'
}
-const getDataSource = (): CallableFunction =>
- function (row: any): string | React.ReactElement {
- const { sourceUrl }: { sourceUrl: string } = row
- return sourceUrl || 'N/A'
- }
-
const getEmbeddingModel = (): CallableFunction =>
function (row: any): string {
const { modelName } = row
@@ -63,7 +57,6 @@ export default function KnowledgeBasesOverviewPage({
name: kb.metadata.name,
teamId,
status: kb.status,
- sourceUrl: kb.spec.sourceUrl,
modelName: kb.spec.modelName,
})) || []
@@ -78,11 +71,6 @@ export default function KnowledgeBasesOverviewPage({
label: t('Status'),
renderer: getStatus(),
},
- {
- id: 'datasource',
- label: t('Data Source'),
- renderer: getDataSource(),
- },
{
id: 'embeddingModel',
label: t('Embedding Model'),
diff --git a/src/redux/otomiApi.ts b/src/redux/otomiApi.ts
index 5d7817f6b..5c538b644 100644
--- a/src/redux/otomiApi.ts
+++ b/src/redux/otomiApi.ts
@@ -6438,9 +6438,14 @@ export type DeleteAplKnowledgeBaseApiArg = {
export type GetAplAgentsApiResponse = /** status 200 Successfully obtained agents */ ({
kind: 'AkamaiAgent'
spec: {
- knowledgeBase?: string
foundationModel: string
agentInstructions: string
+ tools?: {
+ type: string
+ name: string
+ description?: string
+ endpoint?: string
+ }[]
}
} & {
metadata: {
@@ -6468,9 +6473,14 @@ export type GetAplAgentsApiArg = {
export type CreateAplAgentApiResponse = /** status 200 Successfully stored agent configuration */ {
kind: 'AkamaiAgent'
spec: {
- knowledgeBase?: string
foundationModel: string
agentInstructions: string
+ tools?: {
+ type: string
+ name: string
+ description?: string
+ endpoint?: string
+ }[]
}
} & {
metadata: {
@@ -6498,9 +6508,14 @@ export type CreateAplAgentApiArg = {
body: {
kind: 'AkamaiAgent'
spec: {
- knowledgeBase?: string
foundationModel: string
agentInstructions: string
+ tools?: {
+ type: string
+ name: string
+ description?: string
+ endpoint?: string
+ }[]
}
} & {
metadata: {
@@ -6511,9 +6526,14 @@ export type CreateAplAgentApiArg = {
export type GetAplAgentApiResponse = /** status 200 Successfully obtained agent configuration */ {
kind: 'AkamaiAgent'
spec: {
- knowledgeBase?: string
foundationModel: string
agentInstructions: string
+ tools?: {
+ type: string
+ name: string
+ description?: string
+ endpoint?: string
+ }[]
}
} & {
metadata: {
@@ -6543,9 +6563,14 @@ export type GetAplAgentApiArg = {
export type EditAplAgentApiResponse = /** status 200 Successfully edited a team agent */ {
kind: 'AkamaiAgent'
spec: {
- knowledgeBase?: string
foundationModel: string
agentInstructions: string
+ tools?: {
+ type: string
+ name: string
+ description?: string
+ endpoint?: string
+ }[]
}
} & {
metadata: {
@@ -6575,9 +6600,14 @@ export type EditAplAgentApiArg = {
body: {
kind: 'AkamaiAgent'
spec: {
- knowledgeBase?: string
foundationModel: string
agentInstructions: string
+ tools?: {
+ type: string
+ name: string
+ description?: string
+ endpoint?: string
+ }[]
}
} & {
metadata: {
diff --git a/src/setupProxy.js b/src/setupProxy.js
index aa8f95ec3..bf3d43122 100644
--- a/src/setupProxy.js
+++ b/src/setupProxy.js
@@ -9,6 +9,11 @@ module.exports = function (app) {
logLevel: 'debug',
pathRewrite: { '^/api/ws': '/ws' },
}),
+ proxy('/agent', {
+ target: 'http://localhost:9099',
+ pathRewrite: { '^/agent': '' },
+ changeOrigin: true,
+ }),
proxy('/api', {
target: 'http://localhost:8080',
pathRewrite: { '^/api/': '/' },