Jaspers Hub

jaspers / agents

pluginOfficialReviewed★ 0↓ 1

Research Agents: Agents you define that answer research chats from SEC filings, your documents, and the web, citing every passage.

3 KB
1import type { BackendContext } from '@jaspers-ai/sdk'
2import type { Reads } from './sec.ts'
3
4// The research store (JaspersAI/research-store), reached as the plugin's connection agents/research:
5// SEC filings for everyone, the account's own documents, Vertex AI's embeddings and ranking, and SEC
6// EDGAR. The Terminal signs in to it as the Jaspers account. Every call goes through here, and a
7// refusal comes back as the error the store said.
8
9export const SERVER = 'agents/research'
10
11/** A passage a search answered, as the store says it. */
12export interface Hit {
13 id: string
14 doc: string
15 /** Where it is from, in one line: `FLWS 10-K FY2024 · Item 1A Risk Factors · filed 2024-09-06`. */
16 label: string
17 section: string
18 /** The reranker's relevance, 0 to 1, already over the store's floor. */
19 score: number
20 url: string | null
21 text: string
22}
23
24/** An ingest job: a company's kinds of filing and years being read into the store, for everyone. */
25export interface JobStatus {
26 id: number
27 ticker: string | null
28 state: 'queued' | 'listing' | 'reading' | 'done' | 'failed' | 'unknown'
29 total: number
30 done: number
31 failed: number
32 error: string | null
33}
34
35export interface StoredCompany {
36 ticker: string
37 company: string | null
38 cik: number | null
39 filings: number
40 chunks: number
41 unembedded: number
42 /** When SEC last listed its filings, ISO; null before a listing was kept. */
43 recently_as_of: string | null
44 /** The kinds of its filings ever asked for, by anyone, and their years. */
45 reads: Reads
46 /** The job reading it now, if one is. */
47 job: JobStatus | null
48}
49
50export interface ServerStatus {
51 companies: StoredCompany[]
52}
53
54/** One of the account's own documents, or a page its agents read. */
55export interface StoredDoc {
56 id: string
57 origin: 'upload' | 'web'
58 title: string
59 url: string | null
60 chars: number
61 /** ISO. */
62 added_at: string
63}
64
65export async function research<T>(
66 ctx: BackendContext,
67 tool: string,
68 args: Record<string, unknown>,
69 signal?: AbortSignal,
70): Promise<T> {
71 const { text, isError } = await ctx.tools.call(`${SERVER}/${tool}`, args, signal ? { signal } : undefined)
72 if (isError) throw new Error(text)
73 return JSON.parse(text) as T
74}
75
76/** A document as its window opens it. */
77export interface Outline {
78 doc: {
79 id: string
80 origin: string
81 title: string | null
82 url: string | null
83 ticker: string | null
84 form: string | null
85 filing_date: string | null
86 }
87 sections: { seq: number; section: string; title: string; chars: number }[]
88 /** Where the cited chunk sits: its section and its span in that section's text. */
89 chunk: { section: number; start: number; end: number; label: string } | null
90}
91
92/** One section's whole text, read window by window. */
93export async function sectionText(ctx: BackendContext, doc: string, seq: number): Promise<string> {
94 let text = ''
95 let offset: number | null = 0
96 while (offset !== null) {
97 const page: { text: string; next: number | null } = await research(ctx, 'document_section', {
98 doc_id: doc,
99 seq,
100 offset,
101 })
102 text += page.text
103 offset = page.next
104 }
105 return text
106}