typeagent-py 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- typeagent/aitools/auth.py +61 -0
- typeagent/aitools/embeddings.py +232 -0
- typeagent/aitools/utils.py +244 -0
- typeagent/aitools/vectorbase.py +175 -0
- typeagent/knowpro/answer_context_schema.py +49 -0
- typeagent/knowpro/answer_response_schema.py +34 -0
- typeagent/knowpro/answers.py +577 -0
- typeagent/knowpro/collections.py +759 -0
- typeagent/knowpro/common.py +9 -0
- typeagent/knowpro/convknowledge.py +112 -0
- typeagent/knowpro/convsettings.py +94 -0
- typeagent/knowpro/convutils.py +49 -0
- typeagent/knowpro/date_time_schema.py +32 -0
- typeagent/knowpro/field_helpers.py +87 -0
- typeagent/knowpro/fuzzyindex.py +144 -0
- typeagent/knowpro/interfaces.py +818 -0
- typeagent/knowpro/knowledge.py +88 -0
- typeagent/knowpro/kplib.py +125 -0
- typeagent/knowpro/query.py +1128 -0
- typeagent/knowpro/search.py +628 -0
- typeagent/knowpro/search_query_schema.py +165 -0
- typeagent/knowpro/searchlang.py +729 -0
- typeagent/knowpro/searchlib.py +345 -0
- typeagent/knowpro/secindex.py +100 -0
- typeagent/knowpro/serialization.py +390 -0
- typeagent/knowpro/textlocindex.py +179 -0
- typeagent/knowpro/utils.py +17 -0
- typeagent/mcp/server.py +139 -0
- typeagent/podcasts/podcast.py +473 -0
- typeagent/podcasts/podcast_import.py +105 -0
- typeagent/storage/__init__.py +25 -0
- typeagent/storage/memory/__init__.py +13 -0
- typeagent/storage/memory/collections.py +68 -0
- typeagent/storage/memory/convthreads.py +81 -0
- typeagent/storage/memory/messageindex.py +178 -0
- typeagent/storage/memory/propindex.py +289 -0
- typeagent/storage/memory/provider.py +84 -0
- typeagent/storage/memory/reltermsindex.py +318 -0
- typeagent/storage/memory/semrefindex.py +660 -0
- typeagent/storage/memory/timestampindex.py +176 -0
- typeagent/storage/sqlite/__init__.py +31 -0
- typeagent/storage/sqlite/collections.py +362 -0
- typeagent/storage/sqlite/messageindex.py +382 -0
- typeagent/storage/sqlite/propindex.py +119 -0
- typeagent/storage/sqlite/provider.py +293 -0
- typeagent/storage/sqlite/reltermsindex.py +328 -0
- typeagent/storage/sqlite/schema.py +248 -0
- typeagent/storage/sqlite/semrefindex.py +156 -0
- typeagent/storage/sqlite/timestampindex.py +146 -0
- typeagent/storage/utils.py +41 -0
- typeagent_py-0.1.0.dist-info/METADATA +28 -0
- typeagent_py-0.1.0.dist-info/RECORD +55 -0
- typeagent_py-0.1.0.dist-info/WHEEL +5 -0
- typeagent_py-0.1.0.dist-info/licenses/LICENSE +21 -0
- typeagent_py-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,345 @@
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
2
|
+
# Licensed under the MIT License.
|
3
|
+
|
4
|
+
"""
|
5
|
+
INTERNAL LIBRARY
|
6
|
+
Functions that help with creating search and property terms
|
7
|
+
"""
|
8
|
+
|
9
|
+
from typing import cast
|
10
|
+
|
11
|
+
from .interfaces import (
|
12
|
+
ISemanticRefCollection,
|
13
|
+
KnowledgePropertyName,
|
14
|
+
PropertySearchTerm,
|
15
|
+
ScoredSemanticRefOrdinal,
|
16
|
+
SearchTerm,
|
17
|
+
SearchTermGroup,
|
18
|
+
SearchTermGroupTypes,
|
19
|
+
SemanticRef,
|
20
|
+
Term,
|
21
|
+
)
|
22
|
+
from ..storage.memory.propindex import PropertyNames
|
23
|
+
|
24
|
+
|
25
|
+
def create_search_term(
|
26
|
+
text: str,
|
27
|
+
weight: float | None = None,
|
28
|
+
exact_match_value: bool = False,
|
29
|
+
) -> SearchTerm:
|
30
|
+
"""
|
31
|
+
Create a search term with an optional weight
|
32
|
+
|
33
|
+
Args:
|
34
|
+
text: term text
|
35
|
+
weight: optional weight for the term
|
36
|
+
exact_match_value: if True, configures term to only match exactly
|
37
|
+
|
38
|
+
Returns:
|
39
|
+
SearchTerm
|
40
|
+
"""
|
41
|
+
term = Term(text=text, weight=weight)
|
42
|
+
related_terms = [] if exact_match_value else None
|
43
|
+
return SearchTerm(term=term, related_terms=related_terms)
|
44
|
+
|
45
|
+
|
46
|
+
def create_property_search_term(
|
47
|
+
name: str,
|
48
|
+
value: str,
|
49
|
+
exact_match_value: bool = False,
|
50
|
+
) -> PropertySearchTerm:
|
51
|
+
"""
|
52
|
+
Create a new property search term from the given name and value
|
53
|
+
|
54
|
+
Args:
|
55
|
+
name: property name
|
56
|
+
value: property value
|
57
|
+
exact_match_value: if True, configures propertyValue to only match exactly
|
58
|
+
|
59
|
+
Returns:
|
60
|
+
PropertySearchTerm
|
61
|
+
"""
|
62
|
+
# Check if this is one of our well known predefined values
|
63
|
+
if name in (
|
64
|
+
"name",
|
65
|
+
"type",
|
66
|
+
"verb",
|
67
|
+
"subject",
|
68
|
+
"object",
|
69
|
+
"indirectObject",
|
70
|
+
"tag",
|
71
|
+
"topic",
|
72
|
+
):
|
73
|
+
property_name: KnowledgePropertyName | SearchTerm = cast(
|
74
|
+
KnowledgePropertyName, name
|
75
|
+
)
|
76
|
+
else:
|
77
|
+
property_name = create_search_term(name)
|
78
|
+
|
79
|
+
property_value = create_search_term(value)
|
80
|
+
if exact_match_value:
|
81
|
+
# No related terms should be matched for this term
|
82
|
+
property_value.related_terms = []
|
83
|
+
|
84
|
+
return PropertySearchTerm(
|
85
|
+
property_name=property_name, property_value=property_value
|
86
|
+
)
|
87
|
+
|
88
|
+
|
89
|
+
def create_and_term_group(*terms: SearchTermGroupTypes) -> SearchTermGroup:
|
90
|
+
"""
|
91
|
+
Create a term group whose matches are intersected
|
92
|
+
|
93
|
+
Args:
|
94
|
+
terms: search terms to group
|
95
|
+
|
96
|
+
Returns:
|
97
|
+
SearchTermGroup with "and" boolean operation
|
98
|
+
"""
|
99
|
+
return SearchTermGroup(boolean_op="and", terms=list(terms))
|
100
|
+
|
101
|
+
|
102
|
+
def create_or_term_group(*terms: SearchTermGroupTypes) -> SearchTermGroup:
|
103
|
+
"""
|
104
|
+
Create a term group whose matches are union-ed
|
105
|
+
|
106
|
+
Args:
|
107
|
+
terms: search terms to group
|
108
|
+
|
109
|
+
Returns:
|
110
|
+
SearchTermGroup with "or" boolean operation
|
111
|
+
"""
|
112
|
+
return SearchTermGroup(boolean_op="or", terms=list(terms))
|
113
|
+
|
114
|
+
|
115
|
+
def create_or_max_term_group(*terms: SearchTermGroupTypes) -> SearchTermGroup:
|
116
|
+
"""
|
117
|
+
Create an or_max search group
|
118
|
+
|
119
|
+
Args:
|
120
|
+
terms: search terms to group
|
121
|
+
|
122
|
+
Returns:
|
123
|
+
SearchTermGroup with "or_max" boolean operation
|
124
|
+
"""
|
125
|
+
return SearchTermGroup(boolean_op="or_max", terms=list(terms))
|
126
|
+
|
127
|
+
|
128
|
+
def create_search_terms(terms: list[str]) -> list[SearchTerm]:
|
129
|
+
"""
|
130
|
+
Create an array of SearchTerms from the given term strings.
|
131
|
+
You can also provide related terms for each term string by using the following syntax:
|
132
|
+
'novel;book;bestseller': Here, 'book' and 'bestseller' become related terms for 'novel'
|
133
|
+
|
134
|
+
Args:
|
135
|
+
terms: term text, with optional embedded related terms
|
136
|
+
|
137
|
+
Returns:
|
138
|
+
list of SearchTerm
|
139
|
+
"""
|
140
|
+
search_terms: list[SearchTerm] = []
|
141
|
+
for term in terms:
|
142
|
+
search_term = _parse_search_term(term)
|
143
|
+
if search_term:
|
144
|
+
search_terms.append(search_term)
|
145
|
+
return search_terms
|
146
|
+
|
147
|
+
|
148
|
+
def _parse_search_term(text: str) -> SearchTerm | None:
|
149
|
+
"""Parse a search term string with optional related terms separated by ';'."""
|
150
|
+
term_strings = _split_term_values(text, ";")
|
151
|
+
if len(term_strings) > 0:
|
152
|
+
term_strings = [t.lower() for t in term_strings]
|
153
|
+
search_term = SearchTerm(term=Term(text=term_strings[0]))
|
154
|
+
if len(term_strings) > 1:
|
155
|
+
search_term.related_terms = []
|
156
|
+
for i in range(1, len(term_strings)):
|
157
|
+
search_term.related_terms.append(Term(text=term_strings[i]))
|
158
|
+
return search_term
|
159
|
+
return None
|
160
|
+
|
161
|
+
|
162
|
+
def create_property_search_terms(
|
163
|
+
property_name_values: dict[str, str],
|
164
|
+
) -> list[PropertySearchTerm]:
|
165
|
+
"""
|
166
|
+
Create property search from the given record of name, value pairs
|
167
|
+
To search for multiple values for same property name, the value should be a ',' separated list of sub values
|
168
|
+
|
169
|
+
Args:
|
170
|
+
property_name_values: dictionary of property name to value mappings
|
171
|
+
|
172
|
+
Returns:
|
173
|
+
list of PropertySearchTerm
|
174
|
+
"""
|
175
|
+
property_search_terms: list[PropertySearchTerm] = []
|
176
|
+
for property_name, property_value in property_name_values.items():
|
177
|
+
all_values = _split_term_values(property_value, ",")
|
178
|
+
for value in all_values:
|
179
|
+
property_search_terms.append(
|
180
|
+
create_property_search_term(property_name, value)
|
181
|
+
)
|
182
|
+
return property_search_terms
|
183
|
+
|
184
|
+
|
185
|
+
def create_topic_search_term_group(
|
186
|
+
topic_terms: str | list[str],
|
187
|
+
exact_match: bool = False,
|
188
|
+
) -> SearchTermGroup:
|
189
|
+
"""
|
190
|
+
Create a search term group for topic searches.
|
191
|
+
|
192
|
+
Args:
|
193
|
+
topic_terms: single topic term or list of topic terms
|
194
|
+
exact_match: if True, force exact matching
|
195
|
+
|
196
|
+
Returns:
|
197
|
+
SearchTermGroup for topic search
|
198
|
+
"""
|
199
|
+
term_group = create_or_max_term_group()
|
200
|
+
if isinstance(topic_terms, str):
|
201
|
+
term_group.terms.append(
|
202
|
+
create_property_search_term(
|
203
|
+
PropertyNames.Topic.value,
|
204
|
+
topic_terms,
|
205
|
+
exact_match,
|
206
|
+
)
|
207
|
+
)
|
208
|
+
else:
|
209
|
+
for term in topic_terms:
|
210
|
+
term_group.terms.append(
|
211
|
+
create_property_search_term(
|
212
|
+
PropertyNames.Topic.value, term, exact_match
|
213
|
+
)
|
214
|
+
)
|
215
|
+
return term_group
|
216
|
+
|
217
|
+
|
218
|
+
def create_entity_search_term_group(
|
219
|
+
name: str | None = None,
|
220
|
+
type_: str | None = None,
|
221
|
+
facet_name: str | None = None,
|
222
|
+
facet_value: str | None = None,
|
223
|
+
exact_match: bool = False,
|
224
|
+
) -> SearchTermGroup:
|
225
|
+
"""
|
226
|
+
Create a search term group for entity searches.
|
227
|
+
|
228
|
+
Args:
|
229
|
+
name: entity name to search for
|
230
|
+
type_: entity type to search for
|
231
|
+
facet_name: facet name to search for
|
232
|
+
facet_value: facet value to search for
|
233
|
+
exact_match: if True, force exact matching
|
234
|
+
|
235
|
+
Returns:
|
236
|
+
SearchTermGroup for entity search
|
237
|
+
"""
|
238
|
+
term_group = create_or_max_term_group()
|
239
|
+
if name:
|
240
|
+
term_group.terms.append(
|
241
|
+
create_property_search_term(
|
242
|
+
PropertyNames.EntityName.value,
|
243
|
+
name,
|
244
|
+
exact_match,
|
245
|
+
)
|
246
|
+
)
|
247
|
+
if type_:
|
248
|
+
term_group.terms.append(
|
249
|
+
create_property_search_term(
|
250
|
+
PropertyNames.EntityType.value,
|
251
|
+
type_,
|
252
|
+
exact_match,
|
253
|
+
)
|
254
|
+
)
|
255
|
+
if facet_name:
|
256
|
+
term_group.terms.append(
|
257
|
+
create_property_search_term(
|
258
|
+
PropertyNames.FacetName.value,
|
259
|
+
facet_name,
|
260
|
+
exact_match,
|
261
|
+
)
|
262
|
+
)
|
263
|
+
if facet_value:
|
264
|
+
term_group.terms.append(
|
265
|
+
create_property_search_term(
|
266
|
+
PropertyNames.FacetValue.value,
|
267
|
+
facet_value,
|
268
|
+
exact_match,
|
269
|
+
)
|
270
|
+
)
|
271
|
+
return term_group
|
272
|
+
|
273
|
+
|
274
|
+
def create_tag_search_term_group(
|
275
|
+
tags: list[str],
|
276
|
+
exact_match: bool = True,
|
277
|
+
) -> SearchTermGroup:
|
278
|
+
"""
|
279
|
+
Create a search term group for tag searches.
|
280
|
+
|
281
|
+
Args:
|
282
|
+
tags: list of tags to search for
|
283
|
+
exact_match: if True, force exact matching (default True)
|
284
|
+
|
285
|
+
Returns:
|
286
|
+
SearchTermGroup for tag search
|
287
|
+
"""
|
288
|
+
term_group = create_or_max_term_group()
|
289
|
+
for tag in tags:
|
290
|
+
term_group.terms.append(
|
291
|
+
create_property_search_term(PropertyNames.Tag.value, tag, exact_match)
|
292
|
+
)
|
293
|
+
return term_group
|
294
|
+
|
295
|
+
|
296
|
+
def _split_term_values(term: str, split_char: str) -> list[str]:
|
297
|
+
"""Split term values by the given character, trimming and removing empty strings."""
|
298
|
+
# Simple implementation - in TS this uses kpLib.split with trim and removeEmpty options
|
299
|
+
parts = [part.strip() for part in term.split(split_char)]
|
300
|
+
return [part for part in parts if part]
|
301
|
+
|
302
|
+
|
303
|
+
def create_multiple_choice_question(
|
304
|
+
question: str,
|
305
|
+
choices: list[str],
|
306
|
+
add_none: bool = True,
|
307
|
+
) -> str:
|
308
|
+
"""
|
309
|
+
Create a multiple choice question string.
|
310
|
+
|
311
|
+
Args:
|
312
|
+
question: the question text
|
313
|
+
choices: list of choices
|
314
|
+
add_none: if True, add "None of the above" option
|
315
|
+
|
316
|
+
Returns:
|
317
|
+
formatted multiple choice question string
|
318
|
+
"""
|
319
|
+
text = question
|
320
|
+
if len(choices) > 0:
|
321
|
+
text = f"Multiple choice question:\n{question}\n"
|
322
|
+
text += "Answer using *one or more* of the following choices *only*:\n"
|
323
|
+
for choice in choices:
|
324
|
+
text += f"- {choice.strip()}\n"
|
325
|
+
if add_none:
|
326
|
+
text += "- None of the above\n"
|
327
|
+
return text
|
328
|
+
|
329
|
+
|
330
|
+
async def get_semantic_refs_from_scored_ordinals(
|
331
|
+
semantic_refs: ISemanticRefCollection,
|
332
|
+
scored_ordinals: list[ScoredSemanticRefOrdinal],
|
333
|
+
) -> list[SemanticRef]:
|
334
|
+
"""
|
335
|
+
Get semantic references from scored ordinals.
|
336
|
+
|
337
|
+
Args:
|
338
|
+
semantic_refs: collection of semantic references
|
339
|
+
scored_ordinals: list of scored semantic reference ordinals
|
340
|
+
|
341
|
+
Returns:
|
342
|
+
list of SemanticRef objects
|
343
|
+
"""
|
344
|
+
ordinals = [sr.semantic_ref_ordinal for sr in scored_ordinals]
|
345
|
+
return await semantic_refs.get_multiple(ordinals)
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
2
|
+
# Licensed under the MIT License.
|
3
|
+
|
4
|
+
from ..aitools.embeddings import AsyncEmbeddingModel
|
5
|
+
from ..aitools.vectorbase import TextEmbeddingIndexSettings
|
6
|
+
from .convsettings import ConversationSettings
|
7
|
+
from .interfaces import (
|
8
|
+
IConversation,
|
9
|
+
IConversationSecondaryIndexes,
|
10
|
+
IMessage,
|
11
|
+
IStorageProvider,
|
12
|
+
ITermToSemanticRefIndex,
|
13
|
+
TextLocation,
|
14
|
+
)
|
15
|
+
from ..storage.memory.messageindex import build_message_index
|
16
|
+
from ..storage.memory.propindex import PropertyIndex, build_property_index
|
17
|
+
from ..storage.memory.reltermsindex import (
|
18
|
+
RelatedTermsIndex,
|
19
|
+
build_related_terms_index,
|
20
|
+
)
|
21
|
+
from .convsettings import RelatedTermIndexSettings
|
22
|
+
from ..storage.memory.timestampindex import (
|
23
|
+
TimestampToTextRangeIndex,
|
24
|
+
build_timestamp_index,
|
25
|
+
)
|
26
|
+
|
27
|
+
|
28
|
+
class ConversationSecondaryIndexes(IConversationSecondaryIndexes):
|
29
|
+
def __init__(
|
30
|
+
self,
|
31
|
+
storage_provider: IStorageProvider,
|
32
|
+
settings: RelatedTermIndexSettings,
|
33
|
+
):
|
34
|
+
self._storage_provider = storage_provider
|
35
|
+
# Initialize all indexes through storage provider immediately
|
36
|
+
self.property_to_semantic_ref_index = None
|
37
|
+
self.timestamp_index = None
|
38
|
+
self.term_to_related_terms_index = None
|
39
|
+
self.threads = None
|
40
|
+
self.message_index = None
|
41
|
+
|
42
|
+
@classmethod
|
43
|
+
async def create(
|
44
|
+
cls,
|
45
|
+
storage_provider: IStorageProvider,
|
46
|
+
settings: RelatedTermIndexSettings,
|
47
|
+
) -> "ConversationSecondaryIndexes":
|
48
|
+
"""Create and initialize a ConversationSecondaryIndexes with all indexes."""
|
49
|
+
self = cls(storage_provider, settings)
|
50
|
+
# Initialize all indexes from storage provider
|
51
|
+
self.property_to_semantic_ref_index = (
|
52
|
+
await storage_provider.get_property_index()
|
53
|
+
)
|
54
|
+
self.timestamp_index = await storage_provider.get_timestamp_index()
|
55
|
+
self.term_to_related_terms_index = (
|
56
|
+
await storage_provider.get_related_terms_index()
|
57
|
+
)
|
58
|
+
self.threads = await storage_provider.get_conversation_threads()
|
59
|
+
self.message_index = await storage_provider.get_message_text_index()
|
60
|
+
return self
|
61
|
+
|
62
|
+
|
63
|
+
async def build_secondary_indexes[
|
64
|
+
TMessage: IMessage,
|
65
|
+
TTermToSemanticRefIndex: ITermToSemanticRefIndex,
|
66
|
+
](
|
67
|
+
conversation: IConversation[TMessage, TTermToSemanticRefIndex],
|
68
|
+
conversation_settings: ConversationSettings,
|
69
|
+
) -> None:
|
70
|
+
if conversation.secondary_indexes is None:
|
71
|
+
storage_provider = await conversation_settings.get_storage_provider()
|
72
|
+
conversation.secondary_indexes = await ConversationSecondaryIndexes.create(
|
73
|
+
storage_provider, conversation_settings.related_term_index_settings
|
74
|
+
)
|
75
|
+
else:
|
76
|
+
storage_provider = await conversation_settings.get_storage_provider()
|
77
|
+
await build_transient_secondary_indexes(conversation, conversation_settings)
|
78
|
+
await build_related_terms_index(
|
79
|
+
conversation, conversation_settings.related_term_index_settings
|
80
|
+
)
|
81
|
+
if conversation.secondary_indexes is not None:
|
82
|
+
await build_message_index(
|
83
|
+
conversation,
|
84
|
+
storage_provider,
|
85
|
+
)
|
86
|
+
|
87
|
+
|
88
|
+
async def build_transient_secondary_indexes[
|
89
|
+
TMessage: IMessage, TTermToSemanticRefIndex: ITermToSemanticRefIndex
|
90
|
+
](
|
91
|
+
conversation: IConversation[TMessage, TTermToSemanticRefIndex],
|
92
|
+
settings: ConversationSettings,
|
93
|
+
) -> None:
|
94
|
+
if conversation.secondary_indexes is None:
|
95
|
+
conversation.secondary_indexes = await ConversationSecondaryIndexes.create(
|
96
|
+
await settings.get_storage_provider(),
|
97
|
+
(settings.related_term_index_settings),
|
98
|
+
)
|
99
|
+
await build_property_index(conversation)
|
100
|
+
await build_timestamp_index(conversation)
|