AiProjectChat: contexto de equipo + SP/sprint para que la IA asigne HUs

- buildContext() ahora incluye team members del proyecto
  (nombres, roles, célula) desde storage per-project
- HUs incluyen story_points (sp) y sprint para que la IA
  pueda balancear cargas de trabajo
- Épicas incluyen assigned_name y story_points
- System prompt instruye a la IA a:
  · Usar el equipo para asignar HUs
  · Detectar sobrecarga por sprint
  · Sugerir recursos transversales cuando aplique
  · Alertar sobre riesgo de cronograma
This commit is contained in:
2026-05-29 02:20:32 -05:00
parent a1a0e595a4
commit 416784b2fd
+58 -7
View File
@@ -2,8 +2,10 @@
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { chatWithAI } from '@/services/ai' import { chatWithAI } from '@/services/ai'
import { storage } from '@/services/storage'
import { useSettingsStore, AVAILABLE_MODELS, PROVIDER_CONFIG, hasProviderApiKey, type AIProvider } from '@/stores/settings' import { useSettingsStore, AVAILABLE_MODELS, PROVIDER_CONFIG, hasProviderApiKey, type AIProvider } from '@/stores/settings'
import { useWorkItemsStore } from '@/stores/workitems' import { useWorkItemsStore } from '@/stores/workitems'
import { useUsersStore } from '@/stores/users'
import { getSessionsByProject, getProjectState } from '@/services/transcriptions-db' import { getSessionsByProject, getProjectState } from '@/services/transcriptions-db'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
@@ -21,6 +23,7 @@ import { Send, Loader2, AlertCircle, Brain, Settings2, Check, ChevronDown } from
const { t } = useI18n() const { t } = useI18n()
const settings = useSettingsStore() const settings = useSettingsStore()
const workItems = useWorkItemsStore() const workItems = useWorkItemsStore()
const usersStore = useUsersStore()
const props = defineProps({ const props = defineProps({
projectId: { type: Number, required: true }, projectId: { type: Number, required: true },
@@ -53,20 +56,55 @@ function switchModel(provider: AIProvider, modelId: string) {
async function buildContext(): Promise<string> { async function buildContext(): Promise<string> {
const ctx: Record<string, unknown> = {} const ctx: Record<string, unknown> = {}
// 1. HUs: todas, formato compacto (sin límite) // 1. Team members asignados a este proyecto
const teamIds = storage.getJSON<number[]>(`project_team_${props.projectId}`)
if (teamIds && teamIds.length > 0) {
const members = teamIds
.map(id => usersStore.users.find(u => u.id === id))
.filter(Boolean)
.map(u => ({
n: u!.full_name || u!.email,
r: u!.role || '—',
c: u!.cell || '—',
}))
ctx.team = { count: members.length, members }
}
// 2. HUs: todas, con story points, sprint y estado
const hus = workItems.userStories const hus = workItems.userStories
if (hus.length > 0) { if (hus.length > 0) {
const pending = hus.filter(h => !['done','completed','closed','finalizado'].includes(String(h.status ?? '').toLowerCase())) const pending = hus.filter(h => !['done','completed','closed','finalizado'].includes(String(h.status ?? '').toLowerCase()))
ctx.hus = { total: hus.length, pending: pending.length, items: hus.map(h => ({ t: h._cleanTitle || h.title, s: h.status, p: h.priority })) } ctx.hus = {
total: hus.length,
pending: pending.length,
items: hus.map(h => ({
t: h._cleanTitle || h.title,
s: h.status,
p: h.priority,
sp: h.story_points ?? null,
sprint: h.sprint ?? null,
})),
}
} }
// 2. Sesiones: últimas 3 // 3. Épicas con asignación
const epics = workItems.epics
if (epics.length > 0) {
ctx.epics = epics.map(e => ({
n: e._cleanName || e.name || e.title,
s: e.status,
assigned: e.assigned_name || null,
sp: e.story_points ?? null,
}))
}
// 4. Sesiones: últimas 3
const sessions = await getSessionsByProject(props.projectId) const sessions = await getSessionsByProject(props.projectId)
if (sessions.length > 0) { if (sessions.length > 0) {
ctx.sessions = { total: sessions.length, recent: sessions.slice(-3).reverse().map(s => ({ d: s.date, t: s.title })) } ctx.sessions = { total: sessions.length, recent: sessions.slice(-3).reverse().map(s => ({ d: s.date, t: s.title })) }
} }
// 3. Estado // 5. Estado
const state = await getProjectState(props.projectId) const state = await getProjectState(props.projectId)
if (state?.summary) ctx.summary = state.summary.slice(0, 300) if (state?.summary) ctx.summary = state.summary.slice(0, 300)
@@ -84,9 +122,22 @@ async function sendPrompt() {
try { try {
const context = await buildContext() const context = await buildContext()
const systemPrompt = `Proyecto: ${props.projectName}. ${props.projectDescription || ''} Épicas: ${props.epicCount} HUs: ${props.huCount}. const systemPrompt = `Eres un asistente de gestión de proyectos ágiles para el equipo de desarrollo.
Datos del proyecto (JSON): ${context || '{}'}
Respondé en el mismo idioma del usuario. Sé conciso. Si no sabés, decilo.` Proyecto: ${props.projectName}. ${props.projectDescription || ''} Épicas: ${props.epicCount} HUs: ${props.huCount}.
Tienes acceso a estos datos del proyecto (JSON):
${context || '{}'}
El campo "team" contiene los miembros del equipo asignados a este proyecto con su rol. Úsalo para responder quién trabaja en qué, sugerir asignaciones de HUs, y alertar sobre capacidad.
Cada HU puede tener "sp" (story points) y "sprint". Usa esta información para:
- Recomendar asignaciones equilibradas según capacidad del equipo
- Detectar sobrecarga de trabajo en un sprint
- Sugerir recursos adicionales si una HU requiere expertise que no está en el equipo (célula transversal)
- Alertar cuando el cronograma está en riesgo por falta de capacidad
Respondé en el mismo idioma del usuario. Sé conciso y práctico. Si algo no está en los datos, decilo.`
const msgs = [{ role: 'user' as const, content: text }] const msgs = [{ role: 'user' as const, content: text }]
const result = await chatWithAI(msgs, systemPrompt) const result = await chatWithAI(msgs, systemPrompt)