gohumanloop 0.0.2__py3-none-any.whl → 0.0.4__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.
- gohumanloop/adapters/langgraph_adapter.py +65 -52
- gohumanloop/core/interface.py +342 -21
- gohumanloop/core/manager.py +302 -39
- gohumanloop/manager/ghl_manager.py +24 -24
- gohumanloop/providers/api_provider.py +50 -24
- gohumanloop/providers/base.py +170 -9
- gohumanloop/providers/email_provider.py +66 -22
- gohumanloop/providers/terminal_provider.py +95 -23
- gohumanloop/utils/utils.py +33 -3
- {gohumanloop-0.0.2.dist-info → gohumanloop-0.0.4.dist-info}/METADATA +1 -1
- {gohumanloop-0.0.2.dist-info → gohumanloop-0.0.4.dist-info}/RECORD +15 -15
- {gohumanloop-0.0.2.dist-info → gohumanloop-0.0.4.dist-info}/WHEEL +1 -1
- {gohumanloop-0.0.2.dist-info → gohumanloop-0.0.4.dist-info}/entry_points.txt +0 -0
- {gohumanloop-0.0.2.dist-info → gohumanloop-0.0.4.dist-info}/licenses/LICENSE +0 -0
- {gohumanloop-0.0.2.dist-info → gohumanloop-0.0.4.dist-info}/top_level.txt +0 -0
@@ -12,6 +12,7 @@ import logging
|
|
12
12
|
from typing import Dict, Any, Optional, List, Tuple
|
13
13
|
from datetime import datetime
|
14
14
|
from pydantic import SecretStr
|
15
|
+
from concurrent.futures import ThreadPoolExecutor
|
15
16
|
|
16
17
|
from gohumanloop.core.interface import ( HumanLoopResult, HumanLoopStatus, HumanLoopType
|
17
18
|
)
|
@@ -79,6 +80,19 @@ class EmailProvider(BaseProvider):
|
|
79
80
|
|
80
81
|
self._init_language_templates()
|
81
82
|
|
83
|
+
# Create thread pool for background service execution
|
84
|
+
self._executor = ThreadPoolExecutor(max_workers=10)
|
85
|
+
|
86
|
+
|
87
|
+
def __del__(self):
|
88
|
+
"""析构函数,确保线程池被正确关闭"""
|
89
|
+
self._executor.shutdown(wait=False)
|
90
|
+
|
91
|
+
# 取消所有邮件检查任务
|
92
|
+
for task_key, future in list(self._mail_check_tasks.items()):
|
93
|
+
future.cancel()
|
94
|
+
self._mail_check_tasks.clear()
|
95
|
+
|
82
96
|
def _init_language_templates(self):
|
83
97
|
"""初始化不同语言的模板和关键词"""
|
84
98
|
if self.language == "zh":
|
@@ -132,7 +146,7 @@ class EmailProvider(BaseProvider):
|
|
132
146
|
"powered_by": "Powered by GoHumanLoop"
|
133
147
|
}
|
134
148
|
|
135
|
-
async def
|
149
|
+
async def _async_send_email(
|
136
150
|
self,
|
137
151
|
to_email: str,
|
138
152
|
subject: str,
|
@@ -195,7 +209,7 @@ class EmailProvider(BaseProvider):
|
|
195
209
|
logger.exception(f"Unknown error occurred while sending email: {str(e)}")
|
196
210
|
raise
|
197
211
|
|
198
|
-
async def
|
212
|
+
async def _async_check_emails(self, conversation_id: str, request_id: str, recipient_email: str, subject: str):
|
199
213
|
"""Check email replies
|
200
214
|
|
201
215
|
Args:
|
@@ -603,7 +617,7 @@ class EmailProvider(BaseProvider):
|
|
603
617
|
|
604
618
|
return text_body, "\n".join(html_body)
|
605
619
|
|
606
|
-
async def
|
620
|
+
async def async_request_humanloop(
|
607
621
|
self,
|
608
622
|
task_id: str,
|
609
623
|
conversation_id: str,
|
@@ -678,7 +692,7 @@ class EmailProvider(BaseProvider):
|
|
678
692
|
|
679
693
|
|
680
694
|
# 发送邮件
|
681
|
-
success = await self.
|
695
|
+
success = await self._async_send_email(
|
682
696
|
to_email=recipient_email,
|
683
697
|
subject=subject,
|
684
698
|
body=body,
|
@@ -700,14 +714,15 @@ class EmailProvider(BaseProvider):
|
|
700
714
|
self._subject_to_request[subject] = (conversation_id, request_id)
|
701
715
|
|
702
716
|
# 创建邮件检查任务
|
703
|
-
|
704
|
-
|
717
|
+
# 使用线程池执行邮件检查任务,而不是使用asyncio.create_task
|
718
|
+
self._mail_check_tasks[(conversation_id, request_id)] = self._executor.submit(
|
719
|
+
self._run_email_check_task, conversation_id, request_id, recipient_email, subject
|
705
720
|
)
|
706
|
-
|
721
|
+
|
707
722
|
|
708
723
|
# 如果设置了超时,创建超时任务
|
709
724
|
if timeout:
|
710
|
-
self.
|
725
|
+
await self._async_create_timeout_task(conversation_id, request_id, timeout)
|
711
726
|
|
712
727
|
return HumanLoopResult(
|
713
728
|
conversation_id=conversation_id,
|
@@ -715,8 +730,38 @@ class EmailProvider(BaseProvider):
|
|
715
730
|
loop_type=loop_type,
|
716
731
|
status=HumanLoopStatus.PENDING
|
717
732
|
)
|
733
|
+
|
734
|
+
def _run_email_check_task(self, conversation_id: str, request_id: str, recipient_email: str, subject: str):
|
735
|
+
"""Run email check task in thread
|
736
|
+
|
737
|
+
Args:
|
738
|
+
conversation_id: Conversation ID
|
739
|
+
request_id: Request ID
|
740
|
+
recipient_email: Recipient email address
|
741
|
+
subject: Email subject
|
742
|
+
"""
|
743
|
+
# Create new event loop
|
744
|
+
loop = asyncio.new_event_loop()
|
745
|
+
asyncio.set_event_loop(loop)
|
718
746
|
|
719
|
-
|
747
|
+
try:
|
748
|
+
# Run email check in new event loop
|
749
|
+
loop.run_until_complete(
|
750
|
+
self._async_check_emails(conversation_id, request_id, recipient_email, subject)
|
751
|
+
)
|
752
|
+
except Exception as e:
|
753
|
+
logger.error(f"Email check task error: {str(e)}", exc_info=True)
|
754
|
+
# Update request status to error
|
755
|
+
self._update_request_status_error(conversation_id, request_id, f"Email check task error: {str(e)}")
|
756
|
+
finally:
|
757
|
+
# Close event loop
|
758
|
+
loop.close()
|
759
|
+
# Remove from task dictionary
|
760
|
+
if (conversation_id, request_id) in self._mail_check_tasks:
|
761
|
+
del self._mail_check_tasks[(conversation_id, request_id)]
|
762
|
+
|
763
|
+
|
764
|
+
async def async_check_request_status(
|
720
765
|
self,
|
721
766
|
conversation_id: str,
|
722
767
|
request_id: str
|
@@ -755,7 +800,7 @@ class EmailProvider(BaseProvider):
|
|
755
800
|
|
756
801
|
return result
|
757
802
|
|
758
|
-
async def
|
803
|
+
async def async_continue_humanloop(
|
759
804
|
self,
|
760
805
|
conversation_id: str,
|
761
806
|
context: Dict[str, Any],
|
@@ -855,7 +900,7 @@ class EmailProvider(BaseProvider):
|
|
855
900
|
body, html_body = self._format_email_body(prompt, HumanLoopType.CONVERSATION, subject)
|
856
901
|
|
857
902
|
# 发送邮件
|
858
|
-
success = await self.
|
903
|
+
success = await self._async_send_email(
|
859
904
|
to_email=recipient_email,
|
860
905
|
subject=subject,
|
861
906
|
body=body,
|
@@ -876,16 +921,15 @@ class EmailProvider(BaseProvider):
|
|
876
921
|
|
877
922
|
# 存储主题与请求的映射关系
|
878
923
|
self._subject_to_request[subject] = (conversation_id, request_id)
|
879
|
-
|
880
|
-
# 创建邮件检查任务
|
881
|
-
check_task = asyncio.create_task(
|
882
|
-
self._check_emails(conversation_id, request_id, recipient_email, subject)
|
883
|
-
)
|
884
|
-
self._mail_check_tasks[(conversation_id, request_id)] = check_task
|
885
924
|
|
925
|
+
# 使用线程池执行邮件检查任务,而不是使用asyncio.create_task
|
926
|
+
self._mail_check_tasks[(conversation_id, request_id)] = self._executor.submit(
|
927
|
+
self._run_email_check_task, conversation_id, request_id, recipient_email, subject
|
928
|
+
)
|
929
|
+
|
886
930
|
# 如果设置了超时,创建超时任务
|
887
931
|
if timeout:
|
888
|
-
self.
|
932
|
+
await self._async_create_timeout_task(conversation_id, request_id, timeout)
|
889
933
|
|
890
934
|
return HumanLoopResult(
|
891
935
|
conversation_id=conversation_id,
|
@@ -894,7 +938,7 @@ class EmailProvider(BaseProvider):
|
|
894
938
|
status=HumanLoopStatus.PENDING
|
895
939
|
)
|
896
940
|
|
897
|
-
async def
|
941
|
+
async def async_cancel_request(
|
898
942
|
self,
|
899
943
|
conversation_id: str,
|
900
944
|
request_id: str
|
@@ -916,9 +960,9 @@ class EmailProvider(BaseProvider):
|
|
916
960
|
del self._mail_check_tasks[request_key]
|
917
961
|
|
918
962
|
# 调用父类方法取消请求
|
919
|
-
return await super().
|
963
|
+
return await super().async_cancel_request(conversation_id, request_id)
|
920
964
|
|
921
|
-
async def
|
965
|
+
async def async_cancel_conversation(
|
922
966
|
self,
|
923
967
|
conversation_id: str
|
924
968
|
) -> bool:
|
@@ -938,7 +982,7 @@ class EmailProvider(BaseProvider):
|
|
938
982
|
del self._mail_check_tasks[request_key]
|
939
983
|
|
940
984
|
# 调用父类方法取消对话
|
941
|
-
return await super().
|
985
|
+
return await super().async_cancel_conversation(conversation_id)
|
942
986
|
|
943
987
|
def _extract_user_reply_content(self, body: str) -> str:
|
944
988
|
"""Extract the actual reply content from the email, excluding quoted original email content
|
@@ -1,8 +1,6 @@
|
|
1
1
|
import asyncio
|
2
|
-
from
|
3
|
-
import
|
4
|
-
import json
|
5
|
-
from typing import Dict, Any, Optional, List
|
2
|
+
from concurrent.futures import ThreadPoolExecutor
|
3
|
+
from typing import Dict, Any, Optional
|
6
4
|
from datetime import datetime
|
7
5
|
|
8
6
|
from gohumanloop.core.interface import (HumanLoopResult, HumanLoopStatus, HumanLoopType)
|
@@ -22,13 +20,27 @@ class TerminalProvider(BaseProvider):
|
|
22
20
|
name: Provider name
|
23
21
|
config: Configuration options, may include:
|
24
22
|
"""
|
25
|
-
super().__init__(name, config)
|
23
|
+
super().__init__(name, config)
|
24
|
+
|
25
|
+
# Store running terminal input tasks
|
26
|
+
self._terminal_input_tasks = {}
|
27
|
+
# Create thread pool for background service execution
|
28
|
+
self._executor = ThreadPoolExecutor(max_workers=10)
|
29
|
+
|
30
|
+
def __del__(self):
|
31
|
+
"""Destructor to ensure thread pool is properly closed"""
|
32
|
+
self._executor.shutdown(wait=False)
|
33
|
+
|
34
|
+
for task_key, future in list(self._terminal_input_tasks.items()):
|
35
|
+
future.cancel()
|
36
|
+
self._terminal_input_tasks.clear()
|
37
|
+
|
26
38
|
def __str__(self) -> str:
|
27
39
|
base_str = super().__str__()
|
28
40
|
terminal_info = f"- Terminal Provider: Terminal-based human-in-the-loop implementation\n"
|
29
41
|
return f"{terminal_info}{base_str}"
|
30
42
|
|
31
|
-
async def
|
43
|
+
async def async_request_humanloop(
|
32
44
|
self,
|
33
45
|
task_id: str,
|
34
46
|
conversation_id: str,
|
@@ -72,16 +84,32 @@ class TerminalProvider(BaseProvider):
|
|
72
84
|
status=HumanLoopStatus.PENDING
|
73
85
|
)
|
74
86
|
|
75
|
-
|
76
|
-
|
77
|
-
|
87
|
+
|
88
|
+
self._terminal_input_tasks[(conversation_id, request_id)] = self._executor.submit(self._run_async_terminal_interaction, conversation_id, request_id)
|
89
|
+
|
78
90
|
# Create timeout task if timeout is specified
|
79
91
|
if timeout:
|
80
|
-
self.
|
92
|
+
await self._async_create_timeout_task(conversation_id, request_id, timeout)
|
81
93
|
|
82
94
|
return result
|
83
|
-
|
84
|
-
|
95
|
+
|
96
|
+
|
97
|
+
def _run_async_terminal_interaction(self, conversation_id: str, request_id: str):
|
98
|
+
"""Run asynchronous terminal interaction in a separate thread"""
|
99
|
+
# Create new event loop
|
100
|
+
loop = asyncio.new_event_loop()
|
101
|
+
asyncio.set_event_loop(loop)
|
102
|
+
|
103
|
+
try:
|
104
|
+
# Run interaction processing in the new event loop
|
105
|
+
loop.run_until_complete(self._process_terminal_interaction(conversation_id, request_id))
|
106
|
+
finally:
|
107
|
+
loop.close()
|
108
|
+
# Remove from task dictionary
|
109
|
+
if (conversation_id, request_id) in self._terminal_input_tasks:
|
110
|
+
del self._terminal_input_tasks[(conversation_id, request_id)]
|
111
|
+
|
112
|
+
async def async_check_request_status(
|
85
113
|
self,
|
86
114
|
conversation_id: str,
|
87
115
|
request_id: str
|
@@ -120,7 +148,7 @@ class TerminalProvider(BaseProvider):
|
|
120
148
|
|
121
149
|
return result
|
122
150
|
|
123
|
-
async def
|
151
|
+
async def async_continue_humanloop(
|
124
152
|
self,
|
125
153
|
conversation_id: str,
|
126
154
|
context: Dict[str, Any],
|
@@ -175,11 +203,11 @@ class TerminalProvider(BaseProvider):
|
|
175
203
|
)
|
176
204
|
|
177
205
|
# Start async task to process user input
|
178
|
-
|
206
|
+
self._terminal_input_tasks[(conversation_id, request_id)] = self._executor.submit(self._run_async_terminal_interaction, conversation_id, request_id)
|
179
207
|
|
180
208
|
# Create timeout task if timeout is specified
|
181
209
|
if timeout:
|
182
|
-
self.
|
210
|
+
await self._async_create_timeout_task(conversation_id, request_id, timeout)
|
183
211
|
|
184
212
|
return result
|
185
213
|
|
@@ -205,14 +233,14 @@ class TerminalProvider(BaseProvider):
|
|
205
233
|
|
206
234
|
# Handle different interaction types based on loop type
|
207
235
|
if loop_type == HumanLoopType.APPROVAL:
|
208
|
-
await self.
|
236
|
+
await self._async_handle_approval_interaction(conversation_id, request_id, request_info)
|
209
237
|
elif loop_type == HumanLoopType.INFORMATION:
|
210
|
-
await self.
|
238
|
+
await self._async_handle_information_interaction(conversation_id, request_id, request_info)
|
211
239
|
else: # HumanLoopType.CONVERSATION
|
212
|
-
await self.
|
240
|
+
await self._async_handle_conversation_interaction(conversation_id, request_id, request_info)
|
213
241
|
|
214
242
|
|
215
|
-
async def
|
243
|
+
async def _async_handle_approval_interaction(self, conversation_id: str, request_id: str, request_info: Dict[str, Any]):
|
216
244
|
"""Handle approval type interaction
|
217
245
|
|
218
246
|
Args:
|
@@ -225,7 +253,6 @@ class TerminalProvider(BaseProvider):
|
|
225
253
|
# Execute blocking input() call in thread pool using run_in_executor
|
226
254
|
loop = asyncio.get_event_loop()
|
227
255
|
response = await loop.run_in_executor(None, input)
|
228
|
-
|
229
256
|
# Process response
|
230
257
|
response = response.strip().lower()
|
231
258
|
if response in ["approve", "yes", "y", "同意", "批准"]:
|
@@ -239,7 +266,7 @@ class TerminalProvider(BaseProvider):
|
|
239
266
|
else:
|
240
267
|
print("\nInvalid input, please enter 'approve' or 'reject'")
|
241
268
|
# Recursively handle approval interaction
|
242
|
-
await self.
|
269
|
+
await self._async_handle_approval_interaction(conversation_id, request_id, request_info)
|
243
270
|
return
|
244
271
|
|
245
272
|
# Update request information
|
@@ -250,7 +277,7 @@ class TerminalProvider(BaseProvider):
|
|
250
277
|
|
251
278
|
print(f"\nYour decision has been recorded: {status.value}")
|
252
279
|
|
253
|
-
async def
|
280
|
+
async def _async_handle_information_interaction(self, conversation_id: str, request_id: str, request_info: Dict[str, Any]):
|
254
281
|
"""Handle information collection type interaction
|
255
282
|
|
256
283
|
Args:
|
@@ -272,7 +299,7 @@ class TerminalProvider(BaseProvider):
|
|
272
299
|
|
273
300
|
print("\nYour information has been recorded")
|
274
301
|
|
275
|
-
async def
|
302
|
+
async def _async_handle_conversation_interaction(self, conversation_id: str, request_id: str, request_info: Dict[str, Any]):
|
276
303
|
"""Handle conversation type interaction
|
277
304
|
|
278
305
|
Args:
|
@@ -300,3 +327,48 @@ class TerminalProvider(BaseProvider):
|
|
300
327
|
request_info["responded_at"] = datetime.now().isoformat()
|
301
328
|
|
302
329
|
print("\nYour response has been recorded")
|
330
|
+
|
331
|
+
async def async_cancel_request(
|
332
|
+
self,
|
333
|
+
conversation_id: str,
|
334
|
+
request_id: str
|
335
|
+
) -> bool:
|
336
|
+
"""Cancel human-in-the-loop request
|
337
|
+
|
338
|
+
Args:
|
339
|
+
conversation_id: Conversation identifier for multi-turn dialogues
|
340
|
+
request_id: Request identifier for specific interaction request
|
341
|
+
|
342
|
+
Return:
|
343
|
+
bool: Whether cancellation was successful, True indicates successful cancellation,
|
344
|
+
False indicates cancellation failed
|
345
|
+
"""
|
346
|
+
request_key = (conversation_id, request_id)
|
347
|
+
if request_key in self._terminal_input_tasks:
|
348
|
+
self._terminal_input_tasks[request_key].cancel()
|
349
|
+
del self._terminal_input_tasks[request_key]
|
350
|
+
|
351
|
+
# 调用父类方法取消请求
|
352
|
+
return await super().async_cancel_request(conversation_id, request_id)
|
353
|
+
|
354
|
+
async def async_cancel_conversation(
|
355
|
+
self,
|
356
|
+
conversation_id: str
|
357
|
+
) -> bool:
|
358
|
+
"""Cancel the entire conversation
|
359
|
+
|
360
|
+
Args:
|
361
|
+
conversation_id: Conversation identifier
|
362
|
+
|
363
|
+
Returns:
|
364
|
+
bool: Whether cancellation was successful
|
365
|
+
"""
|
366
|
+
# 取消所有相关的邮件检查任务
|
367
|
+
for request_id in self._get_conversation_requests(conversation_id):
|
368
|
+
request_key = (conversation_id, request_id)
|
369
|
+
if request_key in self._terminal_input_tasks:
|
370
|
+
self._terminal_input_tasks[request_key].cancel()
|
371
|
+
del self._terminal_input_tasks[request_key]
|
372
|
+
|
373
|
+
# 调用父类方法取消对话
|
374
|
+
return await super().async_cancel_conversation(conversation_id)
|
gohumanloop/utils/utils.py
CHANGED
@@ -2,16 +2,46 @@ import asyncio
|
|
2
2
|
import os
|
3
3
|
from typing import Optional, Union
|
4
4
|
from pydantic import SecretStr
|
5
|
+
import warnings
|
6
|
+
import logging
|
7
|
+
|
8
|
+
logger = logging.getLogger(__name__)
|
9
|
+
|
5
10
|
def run_async_safely(coro):
|
6
|
-
"""
|
11
|
+
"""
|
12
|
+
Safely run async coroutines in synchronous environment
|
13
|
+
Will raise RuntimeError if called in async environment
|
14
|
+
"""
|
15
|
+
try:
|
16
|
+
loop = asyncio.get_running_loop()
|
17
|
+
except RuntimeError: # No running event loop
|
18
|
+
loop = None
|
19
|
+
|
20
|
+
if loop is not None:
|
21
|
+
raise RuntimeError(
|
22
|
+
"Detected running event loop! "
|
23
|
+
"You should use 'await' directly instead of run_async_safely(). "
|
24
|
+
"If you really need to call sync code from async context, "
|
25
|
+
"consider using asyncio.to_thread() or other proper methods."
|
26
|
+
)
|
27
|
+
|
28
|
+
# Handle synchronous environment
|
7
29
|
try:
|
8
30
|
loop = asyncio.get_event_loop()
|
31
|
+
logger.info("Using existing event loop.")
|
9
32
|
except RuntimeError:
|
10
|
-
# 如果没有事件循环,创建一个新的
|
11
33
|
loop = asyncio.new_event_loop()
|
12
34
|
asyncio.set_event_loop(loop)
|
35
|
+
own_loop = True
|
36
|
+
logger.info("Created new event loop.")
|
37
|
+
else:
|
38
|
+
own_loop = False
|
13
39
|
|
14
|
-
|
40
|
+
try:
|
41
|
+
return loop.run_until_complete(coro)
|
42
|
+
finally:
|
43
|
+
if own_loop and not loop.is_closed():
|
44
|
+
loop.close()
|
15
45
|
|
16
46
|
|
17
47
|
def get_secret_from_env(
|
@@ -1,30 +1,30 @@
|
|
1
1
|
gohumanloop/__init__.py,sha256=7_AkUtiG-_iozObldORElQS9mufxjZx_WfxuX0E5Af0,1845
|
2
2
|
gohumanloop/__main__.py,sha256=zdGKN92H9SgwZfL4xLqPkE1YaiRcHhVg_GqC-H1VurA,75
|
3
3
|
gohumanloop/adapters/__init__.py,sha256=alRiJPahmH5vIbiw7l6o3eFvEADVTkfWYIsXy5uPGSo,391
|
4
|
-
gohumanloop/adapters/langgraph_adapter.py,sha256=
|
4
|
+
gohumanloop/adapters/langgraph_adapter.py,sha256=eJLJ_LgU7ZHwm2zSwlFeDIZxjbr8JgfNZiskv0TDOGg,34355
|
5
5
|
gohumanloop/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
6
|
gohumanloop/cli/main.py,sha256=54-0nwjaAeRH2WhbyO6pN-XADPQwk4_EUUvVWDWruLc,744
|
7
7
|
gohumanloop/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
-
gohumanloop/core/interface.py,sha256=
|
9
|
-
gohumanloop/core/manager.py,sha256=
|
8
|
+
gohumanloop/core/interface.py,sha256=UjeEBKGS_JjwIsT5sBzyq6_IhUkDFrUvBXqpkxkFrAA,22696
|
9
|
+
gohumanloop/core/manager.py,sha256=MAgT5Sx1aLRBIb1mWxp-XJkQxEoibut8-TVtze8bWXQ,33068
|
10
10
|
gohumanloop/manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
|
-
gohumanloop/manager/ghl_manager.py,sha256=
|
11
|
+
gohumanloop/manager/ghl_manager.py,sha256=Td54EcPg1r9Q5JfzS90QjzxiAhcMBYMigaERImLjp7M,21993
|
12
12
|
gohumanloop/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
13
|
gohumanloop/models/api_model.py,sha256=cNTXTlfI7yrTk_87Qf6ms0VtRXO2fYFJFLPTLy2dmQk,2853
|
14
14
|
gohumanloop/models/glh_model.py,sha256=Ht93iCdLfVYz_nW-uW4bE5s0UoyKG3VEx9q-Gg8_tiY,870
|
15
15
|
gohumanloop/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
16
|
-
gohumanloop/providers/api_provider.py,sha256=
|
17
|
-
gohumanloop/providers/base.py,sha256=
|
18
|
-
gohumanloop/providers/email_provider.py,sha256=
|
16
|
+
gohumanloop/providers/api_provider.py,sha256=MPIe_BzXkZmbY8IbLhs04zDG-u2qw0PEy28ZWZxJQAI,25604
|
17
|
+
gohumanloop/providers/base.py,sha256=en6Px3v-tPJz_zA8-cuT69YVoOyO1Av07kpgfeZoejc,22037
|
18
|
+
gohumanloop/providers/email_provider.py,sha256=L_HWkWopJF-8h0e6sOFTZzu2FanBLvCFuXOVZpDahP4,44342
|
19
19
|
gohumanloop/providers/ghl_provider.py,sha256=YdxTpRzitFhTXTbhUcMhQlPUs3kwEBd4wyXEcGK8Svk,2524
|
20
|
-
gohumanloop/providers/terminal_provider.py,sha256=
|
20
|
+
gohumanloop/providers/terminal_provider.py,sha256=L0whpmmMEzI3shV3nOFl4MrEAXEeL06OrUT0GOVzvl0,14663
|
21
21
|
gohumanloop/utils/__init__.py,sha256=idlE5ZNCELVNF9WIiyhtyzG9HJuQQCOlKeTr2aHJ2-Q,56
|
22
22
|
gohumanloop/utils/context_formatter.py,sha256=v4vdgKNJCHjnTtIMq83AkyXwltL14vx-D4KahwcZhIQ,2171
|
23
23
|
gohumanloop/utils/threadsafedict.py,sha256=0-Pmre2-lqHkUPal9wSaqh3fLaEtbo-OnJ3Wbi_knWE,9601
|
24
|
-
gohumanloop/utils/utils.py,sha256=
|
25
|
-
gohumanloop-0.0.
|
26
|
-
gohumanloop-0.0.
|
27
|
-
gohumanloop-0.0.
|
28
|
-
gohumanloop-0.0.
|
29
|
-
gohumanloop-0.0.
|
30
|
-
gohumanloop-0.0.
|
24
|
+
gohumanloop/utils/utils.py,sha256=3f53fHdWLPve-WTn9mGiz3SB0CE7l39caC5Dz0hm85U,2167
|
25
|
+
gohumanloop-0.0.4.dist-info/licenses/LICENSE,sha256=-U5tuCcSpndQwSKWtZbFbazb-_AtZcZL2kQgHbSLg-M,1064
|
26
|
+
gohumanloop-0.0.4.dist-info/METADATA,sha256=mM0-czwhVde-9FdxCAsEgeuXhGxW6f3OxeaTc9sIjNA,1557
|
27
|
+
gohumanloop-0.0.4.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
|
28
|
+
gohumanloop-0.0.4.dist-info/entry_points.txt,sha256=wM6jqRRD8bQXkvIduRVCuAJIlbyWg_F5EDXo5OZ_PwY,88
|
29
|
+
gohumanloop-0.0.4.dist-info/top_level.txt,sha256=LvOXBqS6Mspmcuqp81uz0Vjx_m_YI0w06DOPCiI1BfY,12
|
30
|
+
gohumanloop-0.0.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|