Added clarity to agents, fixed pinia store reset, modified chunk name paths, fixed invite page and routing

This commit is contained in:
Viswamedha Nalabotu 2026-03-22 15:44:13 +00:00
parent 7bad133150
commit fd9bc9db18
9 changed files with 59 additions and 49 deletions

View file

@ -124,9 +124,9 @@ export const API = {
byId: (uuid: string) => `training-file/${uuid}/`, byId: (uuid: string) => `training-file/${uuid}/`,
retry: (uuid: string) => `training-file/${uuid}/retry/`, retry: (uuid: string) => `training-file/${uuid}/retry/`,
}, },
roleRagDocuments: { knowledgeChunks: {
list: () => 'role-rag-document/', list: () => 'knowledge-chunk/',
byId: (uuid: string) => `role-rag-document/${uuid}/`, byId: (uuid: string) => `knowledge-chunk/${uuid}/`,
}, },
}, },

View file

@ -54,7 +54,7 @@ const router = createRouter({
path: '/invite/:inviteUuid', path: '/invite/:inviteUuid',
name: 'invite-accept', name: 'invite-accept',
component: () => import('../views/InviteAccept.vue'), component: () => import('../views/InviteAccept.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true, authRedirect: '/register' },
}, },
{ {
path: '/agents', path: '/agents',
@ -103,7 +103,8 @@ router.beforeEach((to, from, next) => {
return next({ path: '/' }) return next({ path: '/' })
} }
if (to.meta?.requiresAuth && !isAuthenticated) { if (to.meta?.requiresAuth && !isAuthenticated) {
return next({ path: '/login', query: { redirect: to.fullPath } }) const authPath = (to.meta?.authRedirect as string) || '/login'
return next({ path: authPath, query: { redirect: to.fullPath } })
} }
if (to.meta?.requiresManager && !isManager) { if (to.meta?.requiresManager && !isManager) {
return next({ path: '/' }) return next({ path: '/' })

View file

@ -123,6 +123,9 @@ export const useAgentStore = defineStore('agent', () => {
clearReconnectTimer() clearReconnectTimer()
reconnectAttempts = 0 reconnectAttempts = 0
streamBuffer.value = '' streamBuffer.value = ''
eventLog.value = []
executionStatus.value = 'idle'
lastExecutionId.value = null
if (socket.value) { if (socket.value) {
socket.value.close() socket.value.close()

View file

@ -124,6 +124,10 @@ export const useOnboardingAgentStore = defineStore('onboarding-agent', () => {
intentionalClose = false intentionalClose = false
clearReconnectTimer() clearReconnectTimer()
reconnectAttempts = 0 reconnectAttempts = 0
eventLog.value = []
executionStatus.value = 'idle'
lastExecutionId.value = null
currentPhase.value = null
if (socket.value) { if (socket.value) {
socket.value.close() socket.value.close()

View file

@ -2,6 +2,7 @@
import { ref, onMounted, onUnmounted, computed } from 'vue' import { ref, onMounted, onUnmounted, computed } from 'vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { import {
Alert,
Card, Card,
Typography, Typography,
Button, Button,
@ -48,6 +49,13 @@ const agentTypeOptions = [
{ label: 'Assessment Agent', value: 'assessment' }, { label: 'Assessment Agent', value: 'assessment' },
{ label: 'Progress Monitor', value: 'monitor' }, { label: 'Progress Monitor', value: 'monitor' },
] ]
const agentTypeDescriptions: Record<string, string> = {
curriculum: 'Guides new hires through a structured onboarding path — presenting content, tasks, and milestones in a defined sequence for a given role.',
knowledge: 'Answers ad-hoc questions by searching your uploaded training documents and knowledge base. Use this for open-ended Q&A during onboarding.',
assessment: 'Tests understanding through role-specific questions and scenarios, then reports results back to the onboarding session so progress can be tracked.',
monitor: 'Tracks overall session progress and surfaces completions or blockers for manager review without directly interacting with the new hire.',
}
const maxTokens = ref<number>(256) const maxTokens = ref<number>(256)
const queryInput = ref('') const queryInput = ref('')
@ -202,6 +210,13 @@ onUnmounted(() => {
disabled disabled
style="width: 100%" style="width: 100%"
/> />
<Alert
v-if="agentTypeDescriptions[agentForm.agent_type]"
:message="agentTypeDescriptions[agentForm.agent_type]"
type="info"
show-icon
style="margin-top: 6px"
/>
</div> </div>
<div> <div>

View file

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, computed } from 'vue' import { ref, onMounted, computed } from 'vue'
import { List, Typography, Button, Card, Spin, message, Tag, Space, Select } from 'ant-design-vue' import { List, Typography, Button, Card, Spin, message, Tag, Space, Select, Tooltip } from 'ant-design-vue'
import { apiClient, API } from '../router/api' import { apiClient, API } from '../router/api'
import type { MaybePaginated } from '../types/common' import type { MaybePaginated } from '../types/common'
import type { AgentConfig } from '../types/agent' import type { AgentConfig } from '../types/agent'
@ -28,6 +28,13 @@ const fetchAgents = async () => {
} }
} }
const agentTypeDescriptions: Record<string, string> = {
curriculum: 'Guides new hires through a structured onboarding path in sequence.',
knowledge: 'Answers ad-hoc questions by searching uploaded training documents.',
assessment: 'Tests understanding through role-specific questions and reports results.',
monitor: 'Tracks session progress and surfaces completions or blockers.',
}
const getAgentTypeLabel = (type: string) => { const getAgentTypeLabel = (type: string) => {
const types: Record<string, string> = { const types: Record<string, string> = {
curriculum: 'Curriculum Agent', curriculum: 'Curriculum Agent',
@ -120,9 +127,11 @@ onMounted(() => {
<template #description> <template #description>
<Space direction="vertical"> <Space direction="vertical">
<Tag color="geekblue">{{ getRoleLabel(item) }}</Tag> <Tag color="geekblue">{{ getRoleLabel(item) }}</Tag>
<Tag color="blue"> <Tooltip :title="agentTypeDescriptions[item.agent_type]">
{{ getAgentTypeLabel(item.agent_type) }} <Tag color="blue">
</Tag> {{ getAgentTypeLabel(item.agent_type) }}
</Tag>
</Tooltip>
<span class="config-summary"> <span class="config-summary">
Model: {{ item.llm_config?.model_id || 'Default' }} Model: {{ item.llm_config?.model_id || 'Default' }}
</span> </span>

View file

@ -109,14 +109,14 @@ const logos = [
<Divider /> <Divider />
<Row :gutter="16"> <Row :gutter="16">
<Col v-for="stat in stats" :key="stat.title" :xs="24" :sm="8"> <Col v-for="stat in stats" :key="stat.title" :xs="24" :sm="8">
<Card :bordered="false" class="stat-card" hoverable> <Card :bordered="false" class="stat-card">
<Statistic :title="stat.title" :value="stat.value" /> <Statistic :title="stat.title" :value="stat.value" />
</Card> </Card>
</Col> </Col>
</Row> </Row>
</Col> </Col>
<Col :xs="24" :md="10"> <Col :xs="24" :md="10">
<Card class="hero-card" hoverable :cover="null"> <Card class="hero-card" :cover="null">
<img :src="heroImage" alt="Team collaborating" class="hero-img" /> <img :src="heroImage" alt="Team collaborating" class="hero-img" />
<div class="hero-overlay">Adaptive AI playbooks</div> <div class="hero-overlay">Adaptive AI playbooks</div>
</Card> </Card>
@ -135,7 +135,7 @@ const logos = [
<Typography.Title :level="2">Everything you need to ramp faster</Typography.Title> <Typography.Title :level="2">Everything you need to ramp faster</Typography.Title>
<Row :gutter="16"> <Row :gutter="16">
<Col v-for="feature in features" :key="feature.title" :xs="24" :md="8"> <Col v-for="feature in features" :key="feature.title" :xs="24" :md="8">
<Card hoverable class="feature-card"> <Card class="feature-card">
<feature.icon two-tone-color="#2563eb" style="font-size: 28px" /> <feature.icon two-tone-color="#2563eb" style="font-size: 28px" />
<Typography.Title :level="4">{{ feature.title }}</Typography.Title> <Typography.Title :level="4">{{ feature.title }}</Typography.Title>
<Typography.Paragraph>{{ feature.description }}</Typography.Paragraph> <Typography.Paragraph>{{ feature.description }}</Typography.Paragraph>
@ -149,7 +149,7 @@ const logos = [
<Typography.Title :level="2">Prebuilt journeys, tailored in minutes</Typography.Title> <Typography.Title :level="2">Prebuilt journeys, tailored in minutes</Typography.Title>
<Row :gutter="16"> <Row :gutter="16">
<Col v-for="journey in journeys" :key="journey.name" :xs="24" :md="8"> <Col v-for="journey in journeys" :key="journey.name" :xs="24" :md="8">
<Card hoverable class="journey-card"> <Card class="journey-card">
<template #cover> <template #cover>
<img :alt="journey.name" :src="journey.image" /> <img :alt="journey.name" :src="journey.image" />
</template> </template>

View file

@ -1,22 +1,18 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { Card, Button, Spin, message, Result } from 'ant-design-vue' import { Card, Spin, message, Result } from 'ant-design-vue'
import { apiClient, isAxiosError, API } from '../router/api' import { apiClient, API } from '../router/api'
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
const inviteUuid = route.params.inviteUuid as string const inviteUuid = route.params.inviteUuid as string
const loading = ref(false) const accepting = ref(true)
const accepting = ref(false)
const accepted = ref(false) const accepted = ref(false)
const error = ref<string | null>(null)
const joinedOrganizationUuid = ref<string | null>(null) const joinedOrganizationUuid = ref<string | null>(null)
const acceptInvite = async () => { const acceptInvite = async () => {
accepting.value = true
error.value = null
try { try {
const response = await apiClient.post<{ message?: string; organization?: { uuid?: string } }>( const response = await apiClient.post<{ message?: string; organization?: { uuid?: string } }>(
API.invites.join(inviteUuid), API.invites.join(inviteUuid),
@ -25,19 +21,10 @@ const acceptInvite = async () => {
message.success(response.data?.message || 'Successfully joined organization') message.success(response.data?.message || 'Successfully joined organization')
accepted.value = true accepted.value = true
setTimeout(() => { setTimeout(() => {
if (joinedOrganizationUuid.value) { router.push(joinedOrganizationUuid.value ? `/organization/${joinedOrganizationUuid.value}` : '/')
router.push(`/organization/${joinedOrganizationUuid.value}`)
}
else router.push('/')
}, 1500) }, 1500)
} catch (err) { } catch {
console.error('Failed to accept invite:', err) router.push('/')
if (isAxiosError(err)) {
const respErr = err.response?.data?.error || err.response?.data?.detail
error.value = respErr ? String(respErr) : 'Failed to accept invite'
} else {
error.value = 'Failed to accept invite'
}
} finally { } finally {
accepting.value = false accepting.value = false
} }
@ -50,23 +37,14 @@ onMounted(() => {
<template> <template>
<div class="page"> <div class="page">
<Spin :spinning="loading" tip="Loading invite..."> <Spin :spinning="accepting" tip="Accepting invite...">
<Card class="panel" :bordered="false"> <Card class="panel" :bordered="false">
<div v-if="error"> <Result
<Result status="error" :title="error"> v-if="accepted"
<template #extra> status="success"
<Button type="primary" @click="router.push('/')">Go Home</Button> title="Successfully Joined Organization"
</template> sub-title="Redirecting to organization page..."
</Result> />
</div>
<div v-else-if="accepted">
<Result
status="success"
title="Successfully Joined Organization"
sub-title="Redirecting to organization page..."
/>
</div>
</Card> </Card>
</Spin> </Spin>
</div> </div>

View file

@ -936,7 +936,7 @@ onMounted(async () => {
size="small" size="small"
/> />
<Typography.Paragraph v-else type="secondary"> <Typography.Paragraph v-else type="secondary">
No training files uploaded yet. No training files uploaded yet. Use the Upload Training File button to add files you can scope them to a specific role or make them available to all roles.
</Typography.Paragraph> </Typography.Paragraph>
</div> </div>
</Tabs.TabPane> </Tabs.TabPane>