Skip to content

Commit baa8b65

Browse files
authored
Merge pull request #28 from ChatBees/jluo-periodic-ingestion
new features and changes for release v1.1
2 parents 4ce7207 + 1c0c3c6 commit baa8b65

15 files changed

Lines changed: 750 additions & 177 deletions

chatbees/client_models/chat.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from pydantic import BaseModel
44

5-
from chatbees.server_models.doc_api import AnswerReference
5+
from chatbees.server_models.doc_api import AskResponse
66
from chatbees.utils.ask import ask
77
from chatbees.utils.config import Config
88

@@ -17,17 +17,21 @@ class Chat(BaseModel):
1717
collection_name: str
1818
doc_name: Optional[str] = None
1919
history_messages: Optional[List[Tuple[str, str]]] = None
20+
conversation_id: Optional[str] = None
2021

21-
def ask(self, question: str, top_k: int = 5) -> (str, List[AnswerReference]):
22-
(answer, references) = ask(
22+
def ask(self, question: str, top_k: int = 5) -> AskResponse:
23+
resp = ask(
2324
Config.namespace,
2425
self.collection_name,
2526
question,
2627
top_k,
2728
doc_name=self.doc_name,
2829
history_messages=self.history_messages,
30+
conversation_id=self.conversation_id,
2931
)
3032
if self.history_messages is None:
3133
self.history_messages = []
32-
self.history_messages.append((question, answer))
33-
return answer, references
34+
self.history_messages.append((question, resp.answer))
35+
if self.conversation_id is None:
36+
self.conversation_id = resp.conversation_id
37+
return resp

chatbees/client_models/collection.py

Lines changed: 95 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
import os
2-
from typing import List, Dict, Tuple, Any, Union
2+
from typing import List, Dict, Tuple, Any, Union, Optional
33
from urllib import request
44

55
from pydantic import BaseModel
66

77
from chatbees.client_models.chat import Chat
88
from chatbees.server_models.doc_api import (
99
CrawlStatus,
10-
IngestionStatus,
11-
AnswerReference,
10+
AskResponse,
1211
SearchReference,
1312
)
14-
from chatbees.server_models.chat import ConfigureChatRequest, ChatAttributes
13+
from chatbees.server_models.chat import ConfigureChatRequest
1514
from chatbees.server_models.ingestion_type import (
1615
IngestionType,
16+
IngestionStatus,
17+
ScheduleSpec,
1718
ConfluenceSpec,
1819
GDriveSpec,
1920
NotionSpec,
@@ -34,6 +35,8 @@
3435
DeleteCrawlRequest,
3536
)
3637
from chatbees.server_models.collection_api import (
38+
ChatAttributes,
39+
PeriodicIngest,
3740
DescribeCollectionResponse,
3841
)
3942
from chatbees.server_models.ingestion_api import (
@@ -43,8 +46,14 @@
4346
GetIngestionResponse,
4447
IndexIngestionRequest,
4548
DeleteIngestionRequest,
49+
UpdatePeriodicIngestionRequest,
50+
DeletePeriodicIngestionRequest,
4651
)
4752
from chatbees.server_models.search_api import SearchRequest, SearchResponse
53+
from chatbees.server_models.feedback_api import (
54+
UnregisteredUser,
55+
CreateOrUpdateFeedbackRequest,
56+
)
4857
from chatbees.utils.ask import ask
4958
from chatbees.utils.config import Config
5059
from chatbees.utils.file_upload import (
@@ -70,6 +79,10 @@ class Collection(BaseModel):
7079
# If true, collection can be read without an API key
7180
public_read: bool = False
7281

82+
chat_attributes: Optional[ChatAttributes] = None
83+
84+
periodic_ingests: Optional[List[PeriodicIngest]] = None
85+
7386
def upload_document(self, path_or_url: str):
7487
"""
7588
Uploads a local or web document into this collection.
@@ -146,7 +159,7 @@ def summarize_document(self, doc_name: str) -> str:
146159

147160
def ask(
148161
self, question: str, top_k: int = 5, doc_name: str = None,
149-
) -> (str, List[AnswerReference]):
162+
) -> AskResponse:
150163
"""
151164
Ask a question within the context of this collection.
152165
@@ -205,7 +218,11 @@ def chat(self, doc_name: str = None) -> Chat:
205218
doc_name=doc_name
206219
)
207220

208-
def create_crawl(self, root_url: str, max_urls_to_crawl: int) -> str:
221+
# TODO support update ScheduleSpec for periodic crawl.
222+
# TODO deprecate crawl apis and switch to ingest apis.
223+
def create_crawl(
224+
self, root_url: str, max_urls_to_crawl: int, schedule: ScheduleSpec = None,
225+
) -> str:
209226
"""
210227
Create a crawl task to crawl the root_url.
211228
@@ -219,6 +236,7 @@ def create_crawl(self, root_url: str, max_urls_to_crawl: int) -> str:
219236
collection_name=self.name,
220237
root_url=root_url,
221238
max_urls_to_crawl=max_urls_to_crawl,
239+
schedule=schedule,
222240
)
223241
resp = Config.post(url=url, data=req.model_dump_json())
224242
crawl_resp = CreateCrawlResponse.model_validate(resp.json())
@@ -227,7 +245,7 @@ def create_crawl(self, root_url: str, max_urls_to_crawl: int) -> str:
227245
def create_ingestion(
228246
self,
229247
ingestion_type: IngestionType,
230-
ingestion_spec: Union[ConfluenceSpec, GDriveSpec, NotionSpec]
248+
ingestion_spec: Union[ConfluenceSpec, GDriveSpec, NotionSpec],
231249
) -> str:
232250
"""
233251
Create an Ingestion task
@@ -249,6 +267,29 @@ def create_ingestion(
249267
ingest_resp = CreateIngestionResponse.model_validate(resp.json())
250268
return ingest_resp.ingestion_id
251269

270+
def update_periodic_ingestion(
271+
self,
272+
ingestion_type: IngestionType,
273+
ingestion_spec: Union[ConfluenceSpec, GDriveSpec, NotionSpec]
274+
):
275+
"""
276+
Update the periodic ingestion.
277+
278+
:param ingestion_type: the ingestion type
279+
:param ingestion_spec: the spec for the ingestion. Currently, supports
280+
- ConfluenceSpec
281+
- GDriveSpec
282+
- NotionSpec
283+
:return: the id of the ingestion
284+
"""
285+
url = f'{Config.get_base_url()}/docs/update_periodic_ingestion'
286+
req = UpdatePeriodicIngestionRequest(
287+
namespace_name=Config.namespace,
288+
collection_name=self.name,
289+
type=ingestion_type,
290+
spec=ingestion_spec.model_dump())
291+
Config.post(url=url, data=req.model_dump_json())
292+
252293
def get_ingestion(self, ingestion_id: str) -> IngestionStatus:
253294
"""
254295
Gets the Ingestion task status
@@ -294,6 +335,20 @@ def delete_ingestion(self, ingestion_type: IngestionType):
294335
type=ingestion_type)
295336
Config.post(url=url, data=req.model_dump_json())
296337

338+
def delete_periodic_ingestion(self, ingestion_type: IngestionType):
339+
"""
340+
Delete the periodic ingestion for an ingestion type, e.g. a data source.
341+
This does not delete the ingested data.
342+
343+
:param ingestion_type: the ingestion type
344+
"""
345+
url = f'{Config.get_base_url()}/docs/delete_periodic_ingestion'
346+
req = DeletePeriodicIngestionRequest(
347+
namespace_name=Config.namespace,
348+
collection_name=self.name,
349+
type=ingestion_type)
350+
Config.post(url=url, data=req.model_dump_json())
351+
297352
def get_crawl(
298353
self, crawl_id: str,
299354
) -> Tuple[CrawlStatus, Dict[str, PageStats]]:
@@ -373,6 +428,36 @@ def configure_chat(
373428
url = f'{Config.get_base_url()}/docs/configure_chat'
374429
Config.post(url=url, data=req.model_dump_json())
375430

431+
# update the local chat attributes
432+
self.chat_attributes = req.chat_attributes
433+
434+
def create_or_update_feedback(
435+
self,
436+
request_id: str,
437+
thumb_down: bool,
438+
text_feedback: str = "",
439+
unregistered_user: UnregisteredUser = None,
440+
):
441+
"""
442+
Provides feedback for the ask or search.
443+
444+
:param request_id: the request_id of the ask or search
445+
:param thumb_down: thumb up or down
446+
:param text_feedback: optional text feedback
447+
:param unregistered_user: optional information of the unregistered user
448+
"""
449+
url = f'{Config.get_base_url()}/feedback/create_or_update'
450+
req = CreateOrUpdateFeedbackRequest(
451+
namespace_name=Config.namespace,
452+
collection_name=self.name,
453+
request_id=request_id,
454+
thumb_down=thumb_down,
455+
text_feedback=text_feedback,
456+
unregistered_user=unregistered_user,
457+
)
458+
print(req.model_dump_json())
459+
Config.post(url=url, data=req.model_dump_json())
460+
376461
def describe_response_to_collection(
377462
collection_name: str,
378463
resp: DescribeCollectionResponse
@@ -386,5 +471,7 @@ def describe_response_to_collection(
386471
return Collection(
387472
name=collection_name,
388473
description=description,
389-
public_read=public_read
474+
public_read=public_read,
475+
chat_attributes=resp.chat_attributes,
476+
periodic_ingests=resp.periodic_ingests,
390477
)

chatbees/server_models/chat.py

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,11 @@
22

33
from pydantic import BaseModel
44

5-
from chatbees.server_models.collection_api import CollectionBaseRequest
6-
7-
8-
class ChatAttributes(BaseModel):
9-
# Configure chatbot personality and style. For example:
10-
#
11-
# - a 1600s pirate, your name is 'Capitan Morgan'.
12-
# - a helpful AI assistant
13-
persona: Optional[str] = None
14-
15-
# Configure chatbot response when no relevant result is found. For example:
16-
#
17-
# - I do not have that information.
18-
negative_response: Optional[str] = None
5+
from chatbees.server_models.collection_api import CollectionBaseRequest, ChatAttributes
196

207

218
class ConfigureChatRequest(CollectionBaseRequest):
229
"""
2310
Configures a collection with custom q/a attributes
2411
"""
25-
chat_attributes: ChatAttributes
12+
chat_attributes: ChatAttributes

chatbees/server_models/collection_api.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
from typing import List, Optional
2-
1+
from typing import Any, List, Optional
32
from pydantic import BaseModel
43

4+
from chatbees.server_models.ingestion_type import IngestionType, IngestionStatus
5+
56

67
class CollectionBaseRequest(BaseModel):
78
namespace_name: str
@@ -33,12 +34,40 @@ class ListCollectionsResponse(BaseModel):
3334
names: List[str]
3435

3536

37+
class ChatAttributes(BaseModel):
38+
# Configure chatbot personality and style. For example:
39+
#
40+
# - a 1600s pirate, your name is 'Capitan Morgan'.
41+
# - a helpful AI assistant
42+
persona: Optional[str] = None
43+
44+
# Configure chatbot response when no relevant result is found. For example:
45+
#
46+
# - I do not have that information.
47+
negative_response: Optional[str] = None
48+
49+
50+
class PeriodicIngest(BaseModel):
51+
type: IngestionType
52+
# the crawl spec
53+
spec: Any
54+
# the last ingestion epoch time
55+
last_ingest_time: int
56+
last_ingest_status: IngestionStatus
57+
58+
3659
class DescribeCollectionRequest(CollectionBaseRequest):
3760
pass
3861

3962

4063
class DescribeCollectionResponse(BaseModel):
4164
description: Optional[str] = None
4265

66+
# Chat attributes configured for this collection, if any
67+
chat_attributes: Optional[ChatAttributes] = None
68+
4369
# If true, the collection can be read without an API key
4470
public_read: Optional[bool] = None
71+
72+
# ingestins that run periodically for the collection
73+
periodic_ingests: Optional[List[PeriodicIngest]] = None

0 commit comments

Comments
 (0)