Skip to content

Commit 8a96c21

Browse files
authored
Merge pull request #246 from Soulter/feat-edit-message
Support to edit message
2 parents b4685d6 + f1332df commit 8a96c21

2 files changed

Lines changed: 54 additions & 64 deletions

File tree

‎src/hugchat/hugchat.py‎

Lines changed: 50 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,8 @@ def change_conversation(self, conversation_object: Conversation):
261261
self.get_conversation_info(local_conversation)
262262

263263
self.current_conversation = local_conversation
264+
265+
return self.current_conversation
264266

265267
def share_conversation(self, conversation_object: Conversation = None) -> str:
266268
"""
@@ -527,7 +529,8 @@ def get_conversation_info(self, conversation: Union[Conversation, str] = None) -
527529
raise Exception(
528530
f"Failed to get conversation info with status code: {r.status_code}"
529531
)
530-
532+
533+
# you'll never understand the following codes until you try to debug huggingchat in person.
531534
data = r.json()["nodes"][1]["data"]
532535

533536
conversation.model = data[data[0]["model"]]
@@ -545,10 +548,18 @@ def get_conversation_info(self, conversation: Union[Conversation, str] = None) -
545548
data[_node_meta["createdAt"]][1], "%Y-%m-%dT%H:%M:%S.%fZ").timestamp() if 'createdAt' in _node_meta else None
546549
updated_at = datetime.datetime.strptime(
547550
data[_node_meta["updatedAt"]][1], "%Y-%m-%dT%H:%M:%S.%fZ").timestamp() if 'updatedAt' in _node_meta else None
551+
ancestors = []
552+
children = []
553+
for a_idx in data[_node_meta["ancestors"]]:
554+
ancestors.append(data[a_idx])
555+
for c_idx in data[_node_meta["children"]]:
556+
children.append(data[c_idx])
548557
conversation.history.append(MessageNode(
549558
id=data[_node_meta["id"]],
550559
role=data[_node_meta["from"]],
551560
content=data[_node_meta["content"]],
561+
ancestors=ancestors,
562+
children=children,
552563
created_at=created_at,
553564
updated_at=updated_at,
554565
))
@@ -641,41 +652,29 @@ def _stream_query(
641652
self,
642653
text: str,
643654
web_search: bool = False,
644-
temperature: float = 0.1,
645-
top_p: float = 0.95,
646-
repetition_penalty: float = 1.2,
647-
top_k: int = 50,
648-
truncate: int = 1000,
649-
watermark: bool = False,
650-
max_new_tokens: int = 1024,
651-
stop: list = ["</s>"],
652-
return_full_text: bool = False,
653-
use_cache: bool = False,
654655
is_retry: bool = False,
655656
retry_count: int = 5,
656-
# yield all responses from the server.
657-
_stream_yield_all: bool = False,
658657
conversation: Conversation = None,
658+
message_id: str = None,
659659
) -> typing.Generator[dict, None, None]:
660660
if conversation is None:
661661
conversation = self.current_conversation
662662

663663
if retry_count <= 0:
664664
raise Exception(
665665
"the parameter retry_count must be greater than 0.")
666-
if text == "":
667-
raise Exception("the prompt can not be empty.")
668666
if len(conversation.history) == 0:
669667
raise Exception(
670668
"conversation history is empty, but we need the root message id of this conversation to continue.")
671-
672-
# get last message id
673-
last_assistant_message = conversation.history[-1]
674-
logging.debug(
675-
f"conversation {conversation.id} last message id: {last_assistant_message.id}")
669+
670+
if not message_id:
671+
# get last message id
672+
message_id = conversation.history[-1].id
673+
674+
logging.debug(f'message_id: {message_id}')
676675

677676
req_json = {
678-
"id": last_assistant_message.id,
677+
"id": message_id,
679678
"inputs": text,
680679
"is_continue": False,
681680
"is_retry": is_retry,
@@ -751,41 +750,18 @@ def _stream_query(
751750
self.get_conversation_info(conversation)
752751
yield final_answer
753752

754-
def query(
755-
self,
756-
text: str,
757-
web_search: bool = False,
758-
temperature: float = 0.1,
759-
top_p: float = 0.95,
760-
repetition_penalty: float = 1.2,
761-
top_k: int = 50,
762-
truncate: int = 1000,
763-
watermark: bool = False,
764-
max_new_tokens: int = 1024,
765-
stop: list = ["</s>"],
766-
return_full_text: bool = False,
767-
stream: bool = False,
768-
# For stream mode, yield all responses from the server.
769-
_stream_yield_all: bool = False,
770-
use_cache: bool = False,
771-
is_retry: bool = False,
772-
retry_count: int = 5,
773-
conversation: Conversation = None,
774-
) -> Message:
753+
def query(self) -> Message:
775754
"""
776755
**Deprecated**
777-
Same as chat now
756+
Please use `chat()`. The function will raise an error immediately.
778757
"""
779-
if conversation is None:
780-
conversation = self.current_conversation
781-
782-
return self.chat(
783-
text=text,
784-
web_search=web_search,
785-
_stream_yield_all=_stream_yield_all,
786-
retry_count=retry_count,
787-
conversation=conversation,
788-
)
758+
raise Exception("The function is deprecated. Please use `chat()`")
759+
760+
def get_message_node(self, conversation: Conversation, message_id: str):
761+
for node in conversation.history:
762+
if node.id == message_id:
763+
return node
764+
raise Exception(f"no node found which id is {message_id}")
789765

790766
def chat(
791767
self,
@@ -795,34 +771,45 @@ def chat(
795771
_stream_yield_all: bool = False,
796772
retry_count: int = 5,
797773
conversation: Conversation = None,
774+
edit_user_node: MessageNode = None,
798775
*args,
799776
**kvargs,
800777
) -> Message:
801778
"""
802-
Send a message to the current conversation. Return a Message object.
779+
- Send a message to the current conversation. Return a Message object.
803780
804-
You can turn on the web search by set the parameter `web_search` to True
781+
- You can turn on the web search by set the parameter `web_search` to True.
805782
806-
Stream is now the default mode, you can call Message.wait_until_done()
783+
- Stream is now the default mode, you can call Message.wait_until_done() to get the result text.
784+
785+
- `Edit history`: pass `edit_user_node`. The history can be retrieved from `conversation.history`.
807786
808-
About class `Message`:
809-
- `wait_until_done()`: Block until the response done processing or an error raised.
810-
- `__iter__()`: For loop call this Generator and get response.
811-
- `get_search_sources()`: The web search results. It is a list of WebSearchSource objects.
787+
- About class `Message`:
788+
- `wait_until_done()`: Block until the response done processing or an error raised.
789+
- `__iter__()`: For loop call this Generator and get response.
790+
- `get_search_sources()`: The web search results. It is a list of WebSearchSource objects.
812791
813-
For more detail please see Message documentation(Message.__doc__)
792+
- For more detail please see Message documentation(Message.__doc__)
814793
"""
815794
if conversation is None:
816795
conversation = self.current_conversation
796+
797+
if not text:
798+
raise Exception("don't support an empty string(I'm sure LLM cannot understand it)")
799+
if edit_user_node and edit_user_node.role != 'user':
800+
raise Exception("you must pass a user's message node to edit message")
801+
802+
is_retry = True if edit_user_node else False
803+
edit_message_id = edit_user_node.id if is_retry else None
817804

818805
msg = Message(
819806
g=self._stream_query(
820807
text=text,
821808
web_search=web_search,
822-
# For stream mode, yield all responses from the server.
823-
_stream_yield_all=_stream_yield_all,
824809
retry_count=retry_count,
825810
conversation=conversation,
811+
is_retry=is_retry,
812+
message_id=edit_message_id
826813
),
827814
_stream_yield_all=_stream_yield_all,
828815
web_search=web_search,

‎src/hugchat/types/message.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from dataclasses import dataclass
2+
from typing import List
23
from .model import Model
34

45
@dataclass
@@ -9,6 +10,8 @@ class MessageNode:
910
id: str
1011
role: str # "user", "system", or "assistant"
1112
content: str
13+
ancestors: List[str]
14+
children: List[str]
1215
created_at: float # timestamp
1316
updated_at: float # timestamp
1417

@@ -33,7 +36,7 @@ def __init__(
3336
self.title: str = title
3437
self.model = model
3538
self.system_prompt: str = system_prompt
36-
self.history: list = history
39+
self.history: List[MessageNode] = history
3740

3841
def __str__(self) -> str:
3942
return self.id

0 commit comments

Comments
 (0)