File size: 1,711 Bytes
9dfccd9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e646563
9dfccd9
 
e646563
 
 
9dfccd9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import { useState } from 'react'
import { apiFetch, ApiError } from '@/lib/http'
import { useUIStore } from '@/stores/uiStore'

interface Props {
  /** Specific chunk IDs to process. When omitted, all pending chunks are processed. */
  chunkIds?: string[]
  teamId?:   string
}

export function GraphIngestButton({ chunkIds, teamId = 'default' }: Props) {
  const [loading, setLoading] = useState(false)
  const addToast              = useUIStore((s) => s.addToast)

  const run = async () => {
    setLoading(true)
    try {
      const body: Record<string, unknown> = { team_id: teamId }
      if (chunkIds) body.chunk_ids = chunkIds

      const res  = await apiFetch('/graph/ingest', {
        method: 'POST',
        body:   JSON.stringify(body),
      })
      const data = await res.json() as { ingested: number; total: number; failed: number }
      addToast({
        type:    'success',
        message: data.failed > 0
          ? `Graph extraction: ${data.ingested}/${data.total} chunks ingested, ${data.failed} failed`
          : `Ingested ${data.ingested} chunk${data.ingested === 1 ? '' : 's'} into knowledge graph`,
      })
    } catch (err) {
      if (!(err instanceof ApiError)) {
        addToast({ type: 'error', message: 'Graph extraction failed' })
      }
    } finally {
      setLoading(false)
    }
  }

  return (
    <button
      onClick={run}
      disabled={loading}
      className="flex items-center gap-2 rounded-lg bg-brand px-4 py-2 text-sm font-medium text-white hover:bg-brand-dark disabled:opacity-60"
    >
      {loading && <span className="animate-spin" aria-hidden></span>}
      {loading ? 'Extracting entities…' : 'Run graph extraction'}
    </button>
  )
}