microsoft/TypeAgent
Publicmirrored from https://github.com/microsoft/TypeAgentAvailable
python/kp/knowpro/interfaces.py
338lines · modecode
| 1 | # Copyright (c) Microsoft Corporation. |
| 2 | # Licensed under the MIT License. |
| 3 | |
| 4 | # TODO: |
| 5 | # - See TODOs in kplib.py. |
| 6 | # - Do the Protocol classes need to be @runtime_checkable? |
| 7 | # - Should we use ABC instead of Protocol for certain classes? |
| 8 | # |
| 9 | # NOTE: |
| 10 | # - I took some liberty with index types and made them int. |
| 11 | # - I rearranged the order in some cases to ensure def-before-ref. |
| 12 | # - I translated readonly to @property. |
| 13 | |
| 14 | from collections.abc import Sequence |
| 15 | from dataclasses import dataclass, field |
| 16 | from datetime import datetime as Datetime |
| 17 | from typing import Any, Callable, Literal, Protocol, runtime_checkable |
| 18 | |
| 19 | from . import kplib |
| 20 | |
| 21 | |
| 22 | # An object that can provide a KnowledgeResponse structure. |
| 23 | @runtime_checkable |
| 24 | class IKnowledgeSource(Protocol): |
| 25 | def get_knowledge(self) -> kplib.KnowledgeResponse: |
| 26 | raise NotImplementedError |
| 27 | |
| 28 | |
| 29 | @runtime_checkable |
| 30 | class DeletionInfo(Protocol): |
| 31 | timestamp: str |
| 32 | reason: str | None = None |
| 33 | |
| 34 | |
| 35 | type MessageIndex = int |
| 36 | |
| 37 | |
| 38 | @runtime_checkable |
| 39 | class IMessage(IKnowledgeSource, Protocol): |
| 40 | # The text of the message, split into chunks. |
| 41 | text_chunks: list[str] |
| 42 | timestamp: str | None = None |
| 43 | tags: list[str] |
| 44 | deletion_info: DeletionInfo | None = None |
| 45 | |
| 46 | |
| 47 | type SemanticRefIndex = int |
| 48 | |
| 49 | |
| 50 | @runtime_checkable |
| 51 | class ScoredSemanticRef(Protocol): |
| 52 | semantic_ref_index: SemanticRefIndex |
| 53 | score: float |
| 54 | |
| 55 | |
| 56 | @runtime_checkable |
| 57 | class ScoredMessageIndex(Protocol): |
| 58 | message_index: MessageIndex |
| 59 | score: float |
| 60 | |
| 61 | |
| 62 | @runtime_checkable |
| 63 | class ITermToSemanticRefIndex(Protocol): |
| 64 | def get_terms(self) -> Sequence[str]: |
| 65 | raise NotImplementedError |
| 66 | |
| 67 | def add_term( |
| 68 | self, |
| 69 | term: str, |
| 70 | semantic_ref_index: SemanticRefIndex | ScoredSemanticRef, |
| 71 | ) -> None: |
| 72 | raise NotImplementedError |
| 73 | |
| 74 | def remove_term(self, term: str, semantic_ref_index: SemanticRefIndex) -> None: |
| 75 | raise NotImplementedError |
| 76 | |
| 77 | def lookup_term(self, term: str) -> Sequence[ScoredSemanticRef] | None: |
| 78 | raise NotImplementedError |
| 79 | |
| 80 | |
| 81 | type KnowledgeType = Literal["entity", "action", "topic", "tag"] |
| 82 | |
| 83 | |
| 84 | @runtime_checkable |
| 85 | class Topic(Protocol): |
| 86 | text: str |
| 87 | |
| 88 | |
| 89 | @runtime_checkable |
| 90 | class Tag(Protocol): |
| 91 | text: str |
| 92 | |
| 93 | |
| 94 | type Knowledge = kplib.ConcreteEntity | kplib.Action | Topic | Tag |
| 95 | |
| 96 | |
| 97 | @dataclass |
| 98 | class TextLocation: |
| 99 | # The index of the message. |
| 100 | message_index: MessageIndex |
| 101 | # The index of the chunk. |
| 102 | chunk_index: int = 0 |
| 103 | # The index of the character within the chunk. |
| 104 | char_index: int = 0 |
| 105 | |
| 106 | |
| 107 | # A text range within a session. |
| 108 | @dataclass |
| 109 | class TextRange: |
| 110 | # The start of the range. |
| 111 | start: TextLocation |
| 112 | # The end of the range (exclusive). |
| 113 | end: TextLocation | None = None |
| 114 | |
| 115 | |
| 116 | @dataclass |
| 117 | class SemanticRef: |
| 118 | semantic_ref_index: SemanticRefIndex |
| 119 | range: TextRange |
| 120 | knowledge_type: KnowledgeType |
| 121 | knowledge: Knowledge |
| 122 | |
| 123 | |
| 124 | @dataclass |
| 125 | class DateRange: |
| 126 | start: Datetime |
| 127 | # Inclusive. # TODO: Really? Shouldn't this be exclusive? |
| 128 | end: Datetime | None = None |
| 129 | |
| 130 | |
| 131 | @dataclass |
| 132 | class Term: |
| 133 | text: str |
| 134 | # Optional weighting for these matches. |
| 135 | weight: float | None = None |
| 136 | |
| 137 | |
| 138 | @runtime_checkable |
| 139 | class ScoredKnowledge(Protocol): |
| 140 | knowledge_type: KnowledgeType |
| 141 | knowledge: Knowledge |
| 142 | score: float |
| 143 | |
| 144 | |
| 145 | # Allows for faster retrieval of name, value properties |
| 146 | @runtime_checkable |
| 147 | class IPropertyToSemanticRefIndex(Protocol): |
| 148 | def get_values(self) -> Sequence[str]: |
| 149 | raise NotImplementedError |
| 150 | |
| 151 | def add_property( |
| 152 | self, |
| 153 | property_name: str, |
| 154 | value: str, |
| 155 | semantic_ref_index: SemanticRefIndex | ScoredSemanticRef, |
| 156 | ) -> None: |
| 157 | raise NotImplementedError |
| 158 | |
| 159 | def lookup_property( |
| 160 | self, property_name: str, value: str |
| 161 | ) -> Sequence[ScoredSemanticRef] | None: |
| 162 | raise NotImplementedError |
| 163 | |
| 164 | |
| 165 | @runtime_checkable |
| 166 | class TimestampedTextRange(Protocol): |
| 167 | timestamp: str |
| 168 | range: TextRange |
| 169 | |
| 170 | |
| 171 | # Return text ranges in the given date range. |
| 172 | @runtime_checkable |
| 173 | class ITimestampToTextRangeIndex(Protocol): |
| 174 | def add_timestamp(self, message_index: MessageIndex, timestamp: str) -> None: |
| 175 | raise NotImplementedError |
| 176 | |
| 177 | def add_timestamps( |
| 178 | self, message_imestamps: Sequence[tuple[MessageIndex, str]] |
| 179 | ) -> bool: |
| 180 | raise NotImplementedError |
| 181 | |
| 182 | def lookup_range(self, date_range: DateRange) -> Sequence[TimestampedTextRange]: |
| 183 | raise NotImplementedError |
| 184 | |
| 185 | |
| 186 | @runtime_checkable |
| 187 | class ITermToRelatedTerms(Protocol): |
| 188 | def lookup_term(self, text: str) -> Sequence[Term] | None: |
| 189 | raise NotImplementedError |
| 190 | |
| 191 | |
| 192 | @runtime_checkable |
| 193 | class ITermToRelatedTermsFuzzy(Protocol): |
| 194 | async def add_terms( |
| 195 | self, terms: Sequence[str], event_handler: "IndexingEventHandlers | None" = None |
| 196 | ) -> None: |
| 197 | raise NotImplementedError |
| 198 | |
| 199 | async def lookup_term( |
| 200 | self, |
| 201 | text: str, |
| 202 | max_matches: int | None = None, |
| 203 | threshold_score: float | None = None, |
| 204 | ) -> Sequence[Term]: |
| 205 | raise NotImplementedError |
| 206 | |
| 207 | async def lookup_terms( |
| 208 | self, |
| 209 | text_array: Sequence[str], |
| 210 | max_matches: int | None = None, |
| 211 | threshold_score: float | None = None, |
| 212 | ) -> Sequence[Sequence[Term]]: |
| 213 | raise NotImplementedError |
| 214 | |
| 215 | |
| 216 | @runtime_checkable |
| 217 | class ITermToRelatedTermsIndex(Protocol): |
| 218 | @property |
| 219 | def aliases(self) -> ITermToRelatedTerms | None: |
| 220 | raise NotImplementedError |
| 221 | |
| 222 | @property |
| 223 | def fuzzy_index(self) -> ITermToRelatedTermsFuzzy | None: |
| 224 | raise NotImplementedError |
| 225 | |
| 226 | |
| 227 | # A Thread is a set of text ranges in a conversation. |
| 228 | @runtime_checkable |
| 229 | class Thread(Protocol): |
| 230 | description: str |
| 231 | ranges: Sequence[TextRange] |
| 232 | |
| 233 | |
| 234 | type ThreadIndex = int |
| 235 | |
| 236 | |
| 237 | @runtime_checkable |
| 238 | class ScoredThreadIndex(Protocol): |
| 239 | thread_index: ThreadIndex |
| 240 | score: float |
| 241 | |
| 242 | |
| 243 | @runtime_checkable |
| 244 | class IConversationThreads(Protocol): |
| 245 | @property |
| 246 | def threads(self) -> Sequence[Thread]: |
| 247 | raise NotImplementedError |
| 248 | |
| 249 | async def add_thread(self, thread: Thread) -> None: |
| 250 | raise NotImplementedError |
| 251 | |
| 252 | async def lookup_thread( |
| 253 | self, |
| 254 | thread_description: str, |
| 255 | max_matches: int | None = None, |
| 256 | threshold_score: float | None = None, |
| 257 | ) -> Sequence[ScoredThreadIndex] | None: |
| 258 | raise NotImplementedError |
| 259 | |
| 260 | |
| 261 | @runtime_checkable |
| 262 | class IConversationSecondaryIndexes(Protocol): |
| 263 | property_to_semantic_ref_index: IPropertyToSemanticRefIndex | None |
| 264 | timestamp_index: ITimestampToTextRangeIndex | None |
| 265 | terms_to_related_terms_index: ITermToRelatedTermsIndex | None |
| 266 | threads: IConversationThreads | None |
| 267 | |
| 268 | |
| 269 | @runtime_checkable |
| 270 | class IConversation[TMessage: IMessage = Any](Protocol): |
| 271 | name_tag: str |
| 272 | tags: list[str] |
| 273 | messages: list[TMessage] |
| 274 | semantic_refs: list[SemanticRef] | None |
| 275 | semantic_ref_index: ITermToSemanticRefIndex | None |
| 276 | secondary_indexes: IConversationSecondaryIndexes | None |
| 277 | |
| 278 | |
| 279 | # ------------------------ |
| 280 | # Serialization formats |
| 281 | # ------------------------ |
| 282 | |
| 283 | |
| 284 | @runtime_checkable |
| 285 | class ITermToSemanticRefIndexItem(Protocol): |
| 286 | term: str |
| 287 | semantic_ref_indices: Sequence[ScoredSemanticRef] |
| 288 | |
| 289 | |
| 290 | # Persistent form of a term index. |
| 291 | @runtime_checkable |
| 292 | class ITermToSemanticRefIndexData(Protocol): |
| 293 | items: Sequence[ITermToSemanticRefIndexItem] |
| 294 | |
| 295 | |
| 296 | @runtime_checkable |
| 297 | class IConversationData[TMessage = Any](Protocol): |
| 298 | name_tag: str |
| 299 | messages: Sequence[TMessage] |
| 300 | tags: Sequence[str] |
| 301 | semantic_refs: Sequence[SemanticRef] |
| 302 | semantic_index_data: ITermToSemanticRefIndexData | None = None |
| 303 | |
| 304 | |
| 305 | # ------------------------ |
| 306 | # Indexing |
| 307 | # ------------------------ |
| 308 | |
| 309 | |
| 310 | @runtime_checkable |
| 311 | class IndexingEventHandlers(Protocol): |
| 312 | on_knowledge_xtracted: ( |
| 313 | Callable[ |
| 314 | [ |
| 315 | TextLocation, # chunk |
| 316 | kplib.KnowledgeResponse, # knowledge_result |
| 317 | ], |
| 318 | bool, |
| 319 | ] |
| 320 | | None |
| 321 | ) = None |
| 322 | on_embeddings_created: ( |
| 323 | Callable[ |
| 324 | [ |
| 325 | Sequence[str], # source_texts |
| 326 | Sequence[str], # batch |
| 327 | int, # batch_start_at |
| 328 | ], |
| 329 | bool, |
| 330 | ] |
| 331 | | None |
| 332 | ) = None |
| 333 | |
| 334 | |
| 335 | @runtime_checkable |
| 336 | class IndexingResults(Protocol): |
| 337 | chunks_indexed_upto: TextLocation | None = None |
| 338 | error: str | None = None |
| 339 | |