aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/shared/api/models/retrieval/responses.py
blob: f695ebfb57bf407d6a2d3bef4131ec4541c7b69a (about) (plain)
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
from typing import Any, Literal, Optional

from pydantic import BaseModel, Field

from shared.abstractions import (
    AggregateSearchResult,
    ChunkSearchResult,
    GraphSearchResult,
    LLMChatCompletion,
    Message,
    WebPageSearchResult,
)
from shared.api.models.base import R2RResults
from shared.api.models.management.responses import DocumentResponse

from ....abstractions import R2RSerializable


class CitationSpan(R2RSerializable):
    """Represents a single occurrence of a citation in text."""

    start_index: int = Field(
        ..., description="Starting character index of the citation"
    )
    end_index: int = Field(
        ..., description="Ending character index of the citation"
    )
    context_start: int = Field(
        ..., description="Starting index of the surrounding context"
    )
    context_end: int = Field(
        ..., description="Ending index of the surrounding context"
    )


class Citation(R2RSerializable):
    """
    Represents a citation reference in the RAG response.

    The first time a citation appears, it includes the full payload.
    Subsequent appearances only include the citation ID and span information.
    """

    # Basic identification
    id: str = Field(
        ..., description="The short ID of the citation (e.g., 'e41ac2d')"
    )
    object: str = Field(
        "citation", description="The type of object, always 'citation'"
    )

    # Optimize payload delivery
    is_new: bool = Field(
        True,
        description="Whether this is the first occurrence of this citation",
    )

    # Position information
    span: Optional[CitationSpan] = Field(
        None, description="Position of this citation occurrence in the text"
    )

    # Source information - only included for first occurrence
    source_type: Optional[str] = Field(
        None, description="Type of source: 'chunk', 'graph', 'web', or 'doc'"
    )

    # Full payload - only included for first occurrence
    payload: (
        ChunkSearchResult
        | GraphSearchResult
        | WebPageSearchResult
        | DocumentResponse
        | dict[str, Any]
        | None
    ) = Field(
        None,
        description="The complete source object (only included for new citations)",
    )

    class Config:
        extra = "ignore"
        json_schema_extra = {
            "example": {
                "id": "e41ac2d",
                "object": "citation",
                "is_new": True,
                "span": {
                    "start_index": 120,
                    "end_index": 129,
                    "context_start": 80,
                    "context_end": 180,
                },
                "source_type": "chunk",
                "payload": {
                    "id": "e41ac2d1-full-id",
                    "text": "The study found significant improvements...",
                    "metadata": {"title": "Research Paper"},
                },
            }
        }


# class Citation(R2RSerializable):
#     """Represents a single citation reference in the RAG response.

#     Combines both bracket metadata (start/end offsets, snippet range) and the
#     mapped source fields (id, doc ID, chunk text, etc.).
#     """

#     # Bracket references
#     id: str = Field(..., description="The ID of the citation object")
#     object: str = Field(
#         ...,
#         description="The type of object, e.g. `citation`",
#     )
#     payload: (
#         ChunkSearchResult
#         | GraphSearchResult
#         | WebPageSearchResult
#         | DocumentResponse
#         | None
#     ) = Field(
#         ..., description="The object payload and it's corresponding type"
#     )

#     class Config:
#         extra = "ignore"  # This tells Pydantic to ignore extra fields
#         json_schema_extra = {
#             "example": {
#                 "id": "cit.abcd123",
#                 "object": "citation",
#                 "payload": "ChunkSearchResult(...)",
#             }
#         }


class RAGResponse(R2RSerializable):
    generated_answer: str = Field(
        ..., description="The generated completion from the RAG process"
    )
    search_results: AggregateSearchResult = Field(
        ..., description="The search results used for the RAG process"
    )
    citations: Optional[list[Citation]] = Field(
        None,
        description="Structured citation metadata, if you do citation extraction.",
    )
    metadata: dict = Field(
        default_factory=dict,
        description="Additional data returned by the LLM provider",
    )
    completion: str = Field(
        ...,
        description="The generated completion from the RAG process",
        # deprecated=True,
    )

    class Config:
        json_schema_extra = {
            "example": {
                "generated_answer": "The capital of France is Paris.",
                "search_results": {
                    "chunk_search_results": [
                        {
                            "index": 1,
                            "start_index": 25,
                            "end_index": 28,
                            "uri": "https://example.com/doc1",
                            "title": "example_document_1.pdf",
                            "license": "CC-BY-4.0",
                        }
                    ],
                    "graph_search_results": [
                        {
                            "content": {
                                "id": "3f3d47f3-8baf-58eb-8bc2-0171fb1c6e09",
                                "name": "Entity Name",
                                "description": "Entity Description",
                                "metadata": {},
                            },
                            "result_type": "entity",
                            "chunk_ids": [
                                "c68dc72e-fc23-5452-8f49-d7bd46088a96"
                            ],
                            "metadata": {
                                "associated_query": "What is the capital of France?"
                            },
                        }
                    ],
                    "web_search_results": [
                        {
                            "title": "Page Title",
                            "link": "https://example.com/page",
                            "snippet": "Page snippet",
                            "position": 1,
                            "date": "2021-01-01",
                            "sitelinks": [
                                {
                                    "title": "Sitelink Title",
                                    "link": "https://example.com/sitelink",
                                }
                            ],
                        }
                    ],
                    "document_search_results": [
                        {
                            "document": {
                                "id": "3f3d47f3-8baf-58eb-8bc2-0171fb1c6e09",
                                "title": "Document Title",
                                "chunks": ["Chunk 1", "Chunk 2"],
                                "metadata": {},
                            },
                        }
                    ],
                },
                "citations": [
                    {
                        "index": 1,
                        "rawIndex": 9,
                        "startIndex": 393,
                        "endIndex": 396,
                        "snippetStartIndex": 320,
                        "snippetEndIndex": 418,
                        "sourceType": "chunk",
                        "id": "e760bb76-1c6e-52eb-910d-0ce5b567011b",
                        "document_id": "e43864f5-a36f-548e-aacd-6f8d48b30c7f",
                        "owner_id": "2acb499e-8428-543b-bd85-0d9098718220",
                        "collection_ids": [
                            "122fdf6a-e116-546b-a8f6-e4cb2e2c0a09"
                        ],
                        "score": 0.64,
                        "text": "Document Title: DeepSeek_R1.pdf\n\nText: could achieve an accuracy of ...",
                        "metadata": {
                            "title": "DeepSeek_R1.pdf",
                            "license": "CC-BY-4.0",
                            "chunk_order": 68,
                            "document_type": "pdf",
                        },
                    }
                ],
                "metadata": {
                    "id": "chatcmpl-example123",
                    "choices": [
                        {
                            "finish_reason": "stop",
                            "index": 0,
                            "message": {"role": "assistant"},
                        }
                    ],
                },
                "completion": "TO BE DEPRECATED",
            }
        }


class AgentResponse(R2RSerializable):
    messages: list[Message] = Field(..., description="Agent response messages")
    conversation_id: str = Field(
        ..., description="The conversation ID for the RAG agent response"
    )

    class Config:
        json_schema_extra = {
            "example": {
                "messages": [
                    {
                        "role": "assistant",
                        "content": """Aristotle (384–322 BC) was an Ancient
                        Greek philosopher and polymath whose contributions
                        have had a profound impact on various fields of
                        knowledge.
                        Here are some key points about his life and work:
                        \n\n1. **Early Life**: Aristotle was born in 384 BC in
                        Stagira, Chalcidice, which is near modern-day
                        Thessaloniki, Greece. His father, Nicomachus, was the
                        personal physician to King Amyntas of Macedon, which
                        exposed Aristotle to medical and biological knowledge
                        from a young age [C].\n\n2. **Education and Career**:
                        After the death of his parents, Aristotle was sent to
                        Athens to study at Plato's Academy, where he remained
                        for about 20 years. After Plato's death, Aristotle
                        left Athens and eventually became the tutor of
                        Alexander the Great [C].
                        \n\n3. **Philosophical Contributions**: Aristotle
                        founded the Lyceum in Athens, where he established the
                        Peripatetic school of philosophy. His works cover a
                        wide range of subjects, including metaphysics, ethics,
                        politics, logic, biology, and aesthetics. His writings
                        laid the groundwork for many modern scientific and
                        philosophical inquiries [A].\n\n4. **Legacy**:
                        Aristotle's influence extends beyond philosophy to the
                          natural sciences, linguistics, economics, and
                          psychology. His method of systematic observation and
                          analysis has been foundational to the development of
                          modern science [A].\n\nAristotle's comprehensive
                          approach to knowledge and his systematic methodology
                          have earned him a lasting legacy as one of the
                          greatest philosophers of all time.\n\nSources:
                          \n- [A] Aristotle's broad range of writings and
                          influence on modern science.\n- [C] Details about
                          Aristotle's early life and education.""",
                        "name": None,
                        "function_call": None,
                        "tool_calls": None,
                        "metadata": {
                            "citations": [
                                {
                                    "index": 1,
                                    "rawIndex": 9,
                                    "startIndex": 393,
                                    "endIndex": 396,
                                    "snippetStartIndex": 320,
                                    "snippetEndIndex": 418,
                                    "sourceType": "chunk",
                                    "id": "e760bb76-1c6e-52eb-910d-0ce5b567011b",
                                    "document_id": """
                                    e43864f5-a36f-548e-aacd-6f8d48b30c7f
                                    """,
                                    "owner_id": """
                                    2acb499e-8428-543b-bd85-0d9098718220
                                    """,
                                    "collection_ids": [
                                        "122fdf6a-e116-546b-a8f6-e4cb2e2c0a09"
                                    ],
                                    "score": 0.64,
                                    "text": """
                                    Document Title: DeepSeek_R1.pdf
                                    \n\nText: could achieve an accuracy of ...
                                    """,
                                    "metadata": {
                                        "title": "DeepSeek_R1.pdf",
                                        "license": "CC-BY-4.0",
                                        "chunk_order": 68,
                                        "document_type": "pdf",
                                    },
                                }
                            ],
                            "aggregated_search_results": {
                                "chunk_search_results": [
                                    {
                                        "id": "3f3d47f3-8baf-58eb-8bc2-0171fb1c6e09",
                                        "document_id": "3e157b3a-8469-51db-90d9-52e7d896b49b",
                                        "owner_id": "2acb499e-8428-543b-bd85-0d9098718220",
                                        "collection_ids": [],
                                        "score": 0.23943702876567796,
                                        "text": "Example text from the document",
                                        "metadata": {
                                            "title": "example_document.pdf",
                                            "associated_query": "What is the capital of France?",
                                        },
                                    }
                                ],
                                "graph_search_results": [
                                    {
                                        "content": {
                                            "id": "3f3d47f3-8baf-58eb-8bc2-0171fb1c6e09",
                                            "name": "Entity Name",
                                            "description": "Entity Description",
                                            "metadata": {},
                                        },
                                        "result_type": "entity",
                                        "chunk_ids": [
                                            "c68dc72e-fc23-5452-8f49-d7bd46088a96"
                                        ],
                                        "metadata": {
                                            "associated_query": "What is the capital of France?"
                                        },
                                    }
                                ],
                                "web_search_results": [
                                    {
                                        "title": "Page Title",
                                        "link": "https://example.com/page",
                                        "snippet": "Page snippet",
                                        "position": 1,
                                        "date": "2021-01-01",
                                        "sitelinks": [
                                            {
                                                "title": "Sitelink Title",
                                                "link": "https://example.com/sitelink",
                                            }
                                        ],
                                    }
                                ],
                                "document_search_results": [
                                    {
                                        "document": {
                                            "id": "3f3d47f3-8baf-58eb-8bc2-0171fb1c6e09",
                                            "title": "Document Title",
                                            "chunks": ["Chunk 1", "Chunk 2"],
                                            "metadata": {},
                                        },
                                    }
                                ],
                            },
                        },
                    },
                ],
                "conversation_id": "a32b4c5d-6e7f-8a9b-0c1d-2e3f4a5b6c7d",
            }
        }


class DocumentSearchResult(BaseModel):
    document_id: str = Field(
        ...,
        description="The document ID",
    )
    metadata: Optional[dict] = Field(
        None,
        description="The metadata of the document",
    )
    score: float = Field(
        ...,
        description="The score of the document",
    )


# A generic base model for SSE events
class SSEEventBase(BaseModel):
    event: str
    data: Any


# Model for the search results event
class SearchResultsData(BaseModel):
    id: str
    object: str
    data: AggregateSearchResult


class SearchResultsEvent(SSEEventBase):
    event: Literal["search_results"]
    data: SearchResultsData


class DeltaPayload(BaseModel):
    value: str
    annotations: list[Any]


# Model for message events (partial tokens)
class MessageDelta(BaseModel):
    type: str
    payload: DeltaPayload


class Delta(BaseModel):
    content: list[MessageDelta]


class MessageData(BaseModel):
    id: str
    object: str
    delta: Delta


class MessageEvent(SSEEventBase):
    event: Literal["message"]
    data: MessageData


# Update CitationSpan model for SSE events
class CitationSpanData(BaseModel):
    start: int = Field(
        ..., description="Starting character index of the citation"
    )
    end: int = Field(..., description="Ending character index of the citation")
    context_start: Optional[int] = Field(
        None, description="Starting index of surrounding context"
    )
    context_end: Optional[int] = Field(
        None, description="Ending index of surrounding context"
    )


# Update CitationData model
class CitationData(BaseModel):
    id: str = Field(
        ..., description="The short ID of the citation (e.g., 'e41ac2d')"
    )
    object: str = Field(
        "citation", description="The type of object, always 'citation'"
    )

    # New fields from the enhanced Citation model
    is_new: Optional[bool] = Field(
        None,
        description="Whether this is the first occurrence of this citation",
    )

    span: Optional[CitationSpanData] = Field(
        None, description="Position of this citation occurrence in the text"
    )

    source_type: Optional[str] = Field(
        None, description="Type of source: 'chunk', 'graph', 'web', or 'doc'"
    )

    # Optional payload field, only for first occurrence
    payload: Optional[Any] = Field(
        None,
        description="The complete source object (only included for new citations)",
    )

    # For backward compatibility, maintain the existing fields
    class Config:
        populate_by_name = True
        extra = "ignore"


# CitationEvent remains the same, but now using the updated CitationData
class CitationEvent(SSEEventBase):
    event: Literal["citation"]
    data: CitationData


# Model for the final answer event
class FinalAnswerData(BaseModel):
    generated_answer: str
    citations: list[Citation]  # refine if you have a citation model


class FinalAnswerEvent(SSEEventBase):
    event: Literal["final_answer"]
    data: FinalAnswerData


# "tool_call" event
class ToolCallData(BaseModel):
    tool_call_id: str
    name: str
    arguments: Any  # If JSON arguments, use dict[str, Any], or str if needed


class ToolCallEvent(SSEEventBase):
    event: Literal["tool_call"]
    data: ToolCallData


# "tool_result" event
class ToolResultData(BaseModel):
    tool_call_id: str
    role: Literal["tool", "function"]
    content: str


class ToolResultEvent(SSEEventBase):
    event: Literal["tool_result"]
    data: ToolResultData


# Optionally, define a fallback model for unrecognized events
class UnknownEvent(SSEEventBase):
    pass


# 1) Define a new ThinkingEvent type
class ThinkingData(BaseModel):
    id: str
    object: str
    delta: Delta


class ThinkingEvent(SSEEventBase):
    event: str = "thinking"
    data: ThinkingData


# Create a union type for all RAG events
RAGEvent = (
    SearchResultsEvent
    | MessageEvent
    | CitationEvent
    | FinalAnswerEvent
    | UnknownEvent
    | ToolCallEvent
    | ToolResultEvent
    | ToolResultData
    | ToolResultEvent
)

AgentEvent = (
    ThinkingEvent
    | SearchResultsEvent
    | MessageEvent
    | CitationEvent
    | FinalAnswerEvent
    | ToolCallEvent
    | ToolResultEvent
    | UnknownEvent
)

WrappedCompletionResponse = R2RResults[LLMChatCompletion]
# Create wrapped versions of the responses
WrappedVectorSearchResponse = R2RResults[list[ChunkSearchResult]]
WrappedSearchResponse = R2RResults[AggregateSearchResult]
# FIXME: This is returning DocumentResponse, but should be DocumentSearchResult
WrappedDocumentSearchResponse = R2RResults[list[DocumentResponse]]
WrappedRAGResponse = R2RResults[RAGResponse]
WrappedAgentResponse = R2RResults[AgentResponse]
WrappedLLMChatCompletion = R2RResults[LLMChatCompletion]
WrappedEmbeddingResponse = R2RResults[list[float]]