celai 0.1.0__tar.gz

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.
Files changed (130) hide show
  1. celai-0.1.0/PKG-INFO +95 -0
  2. celai-0.1.0/README.md +53 -0
  3. celai-0.1.0/cel/__init__.py +0 -0
  4. celai-0.1.0/cel/assets/celia_logo.png +0 -0
  5. celai-0.1.0/cel/assistants/base_assistant.py +227 -0
  6. celai-0.1.0/cel/assistants/common.py +28 -0
  7. celai-0.1.0/cel/assistants/function_context.py +24 -0
  8. celai-0.1.0/cel/assistants/function_response.py +23 -0
  9. celai-0.1.0/cel/assistants/macaw/custom_chat_models/chat_open_router.py +53 -0
  10. celai-0.1.0/cel/assistants/macaw/macaw_assistant.py +203 -0
  11. celai-0.1.0/cel/assistants/macaw/macaw_history_adapter.py +33 -0
  12. celai-0.1.0/cel/assistants/macaw/macaw_inference_context.py +28 -0
  13. celai-0.1.0/cel/assistants/macaw/macaw_nlp.py +281 -0
  14. celai-0.1.0/cel/assistants/macaw/macaw_settings.py +49 -0
  15. celai-0.1.0/cel/assistants/macaw/macaw_test_chat_model.py +37 -0
  16. celai-0.1.0/cel/assistants/macaw/macaw_utils.py +42 -0
  17. celai-0.1.0/cel/assistants/stream_content_chunk.py +21 -0
  18. celai-0.1.0/cel/cache.py +23 -0
  19. celai-0.1.0/cel/comms/client_command.py +32 -0
  20. celai-0.1.0/cel/comms/sentense_detection.py +116 -0
  21. celai-0.1.0/cel/comms/utils.py +46 -0
  22. celai-0.1.0/cel/config.py +15 -0
  23. celai-0.1.0/cel/connectors/cli/cli_connector.py +169 -0
  24. celai-0.1.0/cel/connectors/cli/model/cli_lead.py +40 -0
  25. celai-0.1.0/cel/connectors/cli/model/cli_message.py +49 -0
  26. celai-0.1.0/cel/connectors/telegram/__init__.py +4 -0
  27. celai-0.1.0/cel/connectors/telegram/model/telegram_attachment.py +173 -0
  28. celai-0.1.0/cel/connectors/telegram/model/telegram_lead.py +46 -0
  29. celai-0.1.0/cel/connectors/telegram/model/telegram_message.py +46 -0
  30. celai-0.1.0/cel/connectors/telegram/samples/hi.mp3 +0 -0
  31. celai-0.1.0/cel/connectors/telegram/samples/hi.oga +0 -0
  32. celai-0.1.0/cel/connectors/telegram/samples/hi.ogg +0 -0
  33. celai-0.1.0/cel/connectors/telegram/samples/message.json +21 -0
  34. celai-0.1.0/cel/connectors/telegram/samples/message_audio.json +27 -0
  35. celai-0.1.0/cel/connectors/telegram/samples/message_callback_query.json +50 -0
  36. celai-0.1.0/cel/connectors/telegram/samples/message_img.json +44 -0
  37. celai-0.1.0/cel/connectors/telegram/samples/message_imgx2.json +44 -0
  38. celai-0.1.0/cel/connectors/telegram/samples/message_location.json +24 -0
  39. celai-0.1.0/cel/connectors/telegram/samples/message_location_place.json +34 -0
  40. celai-0.1.0/cel/connectors/telegram/samples/sample_message.json +21 -0
  41. celai-0.1.0/cel/connectors/telegram/stt_utils.py +53 -0
  42. celai-0.1.0/cel/connectors/telegram/telegram_connector.py +263 -0
  43. celai-0.1.0/cel/connectors/vapi/README.md +15 -0
  44. celai-0.1.0/cel/connectors/vapi/model/vapi_lead.py +63 -0
  45. celai-0.1.0/cel/connectors/vapi/model/vapi_message.py +59 -0
  46. celai-0.1.0/cel/connectors/vapi/sample_data/chunk_vapi_1.json +21 -0
  47. celai-0.1.0/cel/connectors/vapi/sample_data/chunk_vapi_2.json +21 -0
  48. celai-0.1.0/cel/connectors/vapi/sample_data/chunk_vapi_3.json +21 -0
  49. celai-0.1.0/cel/connectors/vapi/sample_data/chunk_vapi_last.json +21 -0
  50. celai-0.1.0/cel/connectors/vapi/sample_data/request.json +31 -0
  51. celai-0.1.0/cel/connectors/vapi/utils.py +26 -0
  52. celai-0.1.0/cel/connectors/vapi/vapi_connector.py +162 -0
  53. celai-0.1.0/cel/connectors/whatsapp/__init__.py +4 -0
  54. celai-0.1.0/cel/connectors/whatsapp/components/component.py +7 -0
  55. celai-0.1.0/cel/connectors/whatsapp/components/document.py +43 -0
  56. celai-0.1.0/cel/connectors/whatsapp/components/document_url.py +40 -0
  57. celai-0.1.0/cel/connectors/whatsapp/components/image.py +50 -0
  58. celai-0.1.0/cel/connectors/whatsapp/components/reply_button.py +31 -0
  59. celai-0.1.0/cel/connectors/whatsapp/components/text.py +28 -0
  60. celai-0.1.0/cel/connectors/whatsapp/components/utils.py +14 -0
  61. celai-0.1.0/cel/connectors/whatsapp/components/video.py +49 -0
  62. celai-0.1.0/cel/connectors/whatsapp/constants.py +1 -0
  63. celai-0.1.0/cel/connectors/whatsapp/functions/buttons.py +114 -0
  64. celai-0.1.0/cel/connectors/whatsapp/functions/utils.py +328 -0
  65. celai-0.1.0/cel/connectors/whatsapp/model/media_utils.py +143 -0
  66. celai-0.1.0/cel/connectors/whatsapp/model/samples/img_caption_message.json +42 -0
  67. celai-0.1.0/cel/connectors/whatsapp/model/samples/img_message.json +42 -0
  68. celai-0.1.0/cel/connectors/whatsapp/model/samples/react_message.json +40 -0
  69. celai-0.1.0/cel/connectors/whatsapp/model/samples/text_message.json +39 -0
  70. celai-0.1.0/cel/connectors/whatsapp/model/whatsapp_attachment.py +254 -0
  71. celai-0.1.0/cel/connectors/whatsapp/model/whatsapp_lead.py +51 -0
  72. celai-0.1.0/cel/connectors/whatsapp/model/whatsapp_message.py +128 -0
  73. celai-0.1.0/cel/connectors/whatsapp/phone_utils.py +10 -0
  74. celai-0.1.0/cel/connectors/whatsapp/utils.py +6 -0
  75. celai-0.1.0/cel/connectors/whatsapp/whatsapp_connector.py +485 -0
  76. celai-0.1.0/cel/decoders/gcp_geocoding.py +30 -0
  77. celai-0.1.0/cel/gateway/__init__.py +2 -0
  78. celai-0.1.0/cel/gateway/message_gateway.py +401 -0
  79. celai-0.1.0/cel/gateway/model/__init__.py +6 -0
  80. celai-0.1.0/cel/gateway/model/attachment.py +93 -0
  81. celai-0.1.0/cel/gateway/model/base_connector.py +36 -0
  82. celai-0.1.0/cel/gateway/model/conversation_lead.py +46 -0
  83. celai-0.1.0/cel/gateway/model/conversation_peer.py +42 -0
  84. celai-0.1.0/cel/gateway/model/message.py +41 -0
  85. celai-0.1.0/cel/gateway/model/message_gateway_context.py +10 -0
  86. celai-0.1.0/cel/gateway/model/outgoing/__init__.py +5 -0
  87. celai-0.1.0/cel/gateway/model/outgoing/outgoing_message.py +54 -0
  88. celai-0.1.0/cel/gateway/model/outgoing/outgoing_message_factory.py +38 -0
  89. celai-0.1.0/cel/gateway/model/outgoing/outgoing_message_link.py +62 -0
  90. celai-0.1.0/cel/gateway/model/outgoing/outgoing_message_select.py +64 -0
  91. celai-0.1.0/cel/gateway/model/outgoing/outgoing_message_text.py +48 -0
  92. celai-0.1.0/cel/gateway/request_context.py +47 -0
  93. celai-0.1.0/cel/message_enhancers/default_message_enhancer.py +23 -0
  94. celai-0.1.0/cel/message_enhancers/smart_message_enhancer_openai.py +115 -0
  95. celai-0.1.0/cel/middlewares/__init__.py +6 -0
  96. celai-0.1.0/cel/middlewares/deepgram_stt.py +63 -0
  97. celai-0.1.0/cel/middlewares/geodecoding.py +48 -0
  98. celai-0.1.0/cel/middlewares/in_mem_blacklist.py +43 -0
  99. celai-0.1.0/cel/middlewares/redis_blacklist.py +44 -0
  100. celai-0.1.0/cel/middlewares/redis_blacklist_async.py +44 -0
  101. celai-0.1.0/cel/middlewares/session_middleware.py +141 -0
  102. celai-0.1.0/cel/model/common.py +37 -0
  103. celai-0.1.0/cel/prompt/prompt_template.py +67 -0
  104. celai-0.1.0/cel/rag/__init__.py +0 -0
  105. celai-0.1.0/cel/rag/providers/markdown_rag.py +118 -0
  106. celai-0.1.0/cel/rag/providers/rag_retriever.py +15 -0
  107. celai-0.1.0/cel/rag/slicers/__init__.py +0 -0
  108. celai-0.1.0/cel/rag/slicers/base_slicer.py +37 -0
  109. celai-0.1.0/cel/rag/slicers/markdown/__init__.py +1 -0
  110. celai-0.1.0/cel/rag/slicers/markdown/markdown.py +117 -0
  111. celai-0.1.0/cel/rag/slicers/markdown/utils.py +74 -0
  112. celai-0.1.0/cel/rag/slicers/utils.py +0 -0
  113. celai-0.1.0/cel/rag/stores/chroma/chroma_store.py +113 -0
  114. celai-0.1.0/cel/rag/stores/vector_store.py +57 -0
  115. celai-0.1.0/cel/rag/text2vec/cached_openai.py +76 -0
  116. celai-0.1.0/cel/rag/text2vec/utils.py +19 -0
  117. celai-0.1.0/cel/stores/common/async_cache_aside_redis.py +146 -0
  118. celai-0.1.0/cel/stores/common/cache_aside_redis.py +162 -0
  119. celai-0.1.0/cel/stores/common/key_value_store.py +45 -0
  120. celai-0.1.0/cel/stores/common/list_redis_store_async.py +36 -0
  121. celai-0.1.0/cel/stores/common/memory_cache.py +82 -0
  122. celai-0.1.0/cel/stores/history/base_history_provider.py +24 -0
  123. celai-0.1.0/cel/stores/history/history_inmemory_provider.py +50 -0
  124. celai-0.1.0/cel/stores/history/history_redis_provider.py +57 -0
  125. celai-0.1.0/cel/stores/history/history_redis_provider_async.py +55 -0
  126. celai-0.1.0/cel/stores/state/base_state_provider.py +31 -0
  127. celai-0.1.0/cel/stores/state/state_inmemory_provider.py +43 -0
  128. celai-0.1.0/cel/stores/state/state_redis_provider.py +58 -0
  129. celai-0.1.0/cel/voice/deepgram_adapter.py +34 -0
  130. celai-0.1.0/pyproject.toml +62 -0
celai-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,95 @@
1
+ Metadata-Version: 2.1
2
+ Name: celai
3
+ Version: 0.1.0
4
+ Summary:
5
+ Author: Alex Martin
6
+ Author-email: alejamp@gmail.com
7
+ Requires-Python: >=3.11,<4.0
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Requires-Dist: aiogram (>=3.6.0)
14
+ Requires-Dist: aioredis (>=2.0.1)
15
+ Requires-Dist: beautifulsoup4 (>=4.12.3)
16
+ Requires-Dist: chardet (>=5.2.0)
17
+ Requires-Dist: chromadb (>=0.5.0)
18
+ Requires-Dist: deepgram-sdk (>=3.2.7)
19
+ Requires-Dist: dictdiffer (>=0.9.0)
20
+ Requires-Dist: diskcache (>=5.6.3)
21
+ Requires-Dist: fakeredis (>=2.23.2)
22
+ Requires-Dist: geopy (>=2.4.1)
23
+ Requires-Dist: halo (>=0.0.31)
24
+ Requires-Dist: langchain (>=0.2.0)
25
+ Requires-Dist: langchain-chroma (>=0.1.1)
26
+ Requires-Dist: langchain-community (>=0.2.1)
27
+ Requires-Dist: langchain-openai (>=0.1.8)
28
+ Requires-Dist: loguru (>=0.7.2)
29
+ Requires-Dist: lxml (>=5.2.2)
30
+ Requires-Dist: marko (>=2.1.0,<3.0.0)
31
+ Requires-Dist: numpy (>=1.26.4,<2.0.0)
32
+ Requires-Dist: openai (>=1.30.1)
33
+ Requires-Dist: openai-responses (>=0.3.2)
34
+ Requires-Dist: pybars3 (>=0.9.7)
35
+ Requires-Dist: pysbd (>=0.3.4)
36
+ Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
37
+ Requires-Dist: redis (>=4.6.0)
38
+ Requires-Dist: shortuuid (>=1.0.13)
39
+ Project-URL: Homepage, https://github.com/cel-ai/celai
40
+ Project-URL: Issues, https://github.com/cel-ai/celai/issues
41
+ Description-Content-Type: text/markdown
42
+
43
+ <!-- A centered logo of celia -->
44
+ <p align="center">
45
+ <img src="cel/assets/celia_logo.png" width="250" />
46
+ </p>
47
+
48
+ # Introduction
49
+
50
+
51
+ Cel.ai is a powerful Python framework designed to accelerate the development of omnichannel virtual assistants. Whether you need to integrate with platforms like WhatsApp, Telegram, or VoIP services such as VAPI.com, Cel.ai provides the tools and flexibility to get your assistant up and running quickly.
52
+
53
+
54
+ ## Install
55
+
56
+ pip install from github:
57
+ ```bash
58
+ pip install git+https://github.com/cel-ai/celai
59
+ ```
60
+ ## Getting Started
61
+
62
+ Create a new Python file, `assistant.py`, and open it in your favorite text editor. Add the following code to set up a basic assistant:
63
+
64
+
65
+ ```python
66
+ # Import Cel.ai modules
67
+ from cel.connectors.cli.cli_connector import CliConnector
68
+ from cel.gateway.message_gateway import MessageGateway, StreamMode
69
+ from cel.message_enhancers.smart_message_enhancer_openai import SmartMessageEnhancerOpenAI
70
+ from cel.assistants.macaw.macaw_assistant import MacawAssistant
71
+ from cel.prompt.prompt_template import PromptTemplate
72
+
73
+ # Setup prompt
74
+ prompt = """You are an AI assistant. Called Celia. You can help a user to buy Bitcoins."""
75
+ prompt_template = PromptTemplate(prompt)
76
+
77
+ # Create the assistant based on the Macaw Assistant
78
+ ast = MacawAssistant(
79
+ prompt=prompt_template
80
+ )
81
+
82
+ # Create the Message Gateway - This component is the core of the assistant
83
+ # It handles the communication between the assistant and the connectors
84
+ gateway = MessageGateway(ast)
85
+
86
+ # For this example, we will use the CLI connector
87
+ conn = CliConnector(
88
+ stream_mode=StreamMode.FULL
89
+ )
90
+ # Register the connector with the gateway
91
+ gateway.register_connector(conn)
92
+
93
+ # Then start the gateway and begin processing messages
94
+ gateway.run()
95
+ ```
celai-0.1.0/README.md ADDED
@@ -0,0 +1,53 @@
1
+ <!-- A centered logo of celia -->
2
+ <p align="center">
3
+ <img src="cel/assets/celia_logo.png" width="250" />
4
+ </p>
5
+
6
+ # Introduction
7
+
8
+
9
+ Cel.ai is a powerful Python framework designed to accelerate the development of omnichannel virtual assistants. Whether you need to integrate with platforms like WhatsApp, Telegram, or VoIP services such as VAPI.com, Cel.ai provides the tools and flexibility to get your assistant up and running quickly.
10
+
11
+
12
+ ## Install
13
+
14
+ pip install from github:
15
+ ```bash
16
+ pip install git+https://github.com/cel-ai/celai
17
+ ```
18
+ ## Getting Started
19
+
20
+ Create a new Python file, `assistant.py`, and open it in your favorite text editor. Add the following code to set up a basic assistant:
21
+
22
+
23
+ ```python
24
+ # Import Cel.ai modules
25
+ from cel.connectors.cli.cli_connector import CliConnector
26
+ from cel.gateway.message_gateway import MessageGateway, StreamMode
27
+ from cel.message_enhancers.smart_message_enhancer_openai import SmartMessageEnhancerOpenAI
28
+ from cel.assistants.macaw.macaw_assistant import MacawAssistant
29
+ from cel.prompt.prompt_template import PromptTemplate
30
+
31
+ # Setup prompt
32
+ prompt = """You are an AI assistant. Called Celia. You can help a user to buy Bitcoins."""
33
+ prompt_template = PromptTemplate(prompt)
34
+
35
+ # Create the assistant based on the Macaw Assistant
36
+ ast = MacawAssistant(
37
+ prompt=prompt_template
38
+ )
39
+
40
+ # Create the Message Gateway - This component is the core of the assistant
41
+ # It handles the communication between the assistant and the connectors
42
+ gateway = MessageGateway(ast)
43
+
44
+ # For this example, we will use the CLI connector
45
+ conn = CliConnector(
46
+ stream_mode=StreamMode.FULL
47
+ )
48
+ # Register the connector with the gateway
49
+ gateway.register_connector(conn)
50
+
51
+ # Then start the gateway and begin processing messages
52
+ gateway.run()
53
+ ```
File without changes
Binary file
@@ -0,0 +1,227 @@
1
+ from abc import ABC, abstractmethod
2
+ import inspect
3
+
4
+ from loguru import logger as log
5
+ from cel.assistants.function_context import FunctionContext
6
+ from cel.assistants.function_response import FunctionResponse
7
+ from cel.gateway.model.base_connector import BaseConnector
8
+ from cel.assistants.common import EventResponse, FunctionDefinition
9
+ from cel.gateway.model.message import Message, ConversationLead
10
+ from cel.prompt.prompt_template import PromptTemplate
11
+ from cel.rag.providers.rag_retriever import RAGRetriever
12
+
13
+
14
+ class Events:
15
+ START: str = "start"
16
+ NEW_MESSAGE: str = "new_message"
17
+ IMAGE: str = "image"
18
+ AUDIO: str = "audio"
19
+ END: str = "end"
20
+
21
+ class BaseAssistant(ABC):
22
+
23
+ def __init__(self, prompt: PromptTemplate = None):
24
+
25
+ self.function_handlers = {}
26
+ self.event_handlers = {}
27
+ self.client_commands_handlers = {}
28
+ self.timeout_handlers = {}
29
+ self.rag_retriever: RAGRetriever = None
30
+ if prompt:
31
+ assert isinstance(prompt, PromptTemplate), "prompt must be an instance of PromptTemplate"
32
+ self.prompt = prompt or PromptTemplate('')
33
+
34
+
35
+
36
+ def set_rag_retrieval(self, rag_retrieval: RAGRetriever):
37
+ # check type
38
+ assert isinstance(rag_retrieval, RAGRetriever), "rag_retrieval must be an instance of RAGRetriever"
39
+ self.rag_retriever = rag_retrieval
40
+
41
+
42
+ def function(self, name, desc, params):
43
+ def decorator(func):
44
+ self.function_handlers[name] = {
45
+ # 'desc': desc,
46
+ # 'params': params,
47
+ 'definition': FunctionDefinition(name=name, description=desc, parameters=params),
48
+ 'func': func
49
+ }
50
+ def wrapper(*args, **kwargs):
51
+ return func(*args, **kwargs)
52
+
53
+ return wrapper
54
+ return decorator
55
+
56
+
57
+ def event(self, name: Events | str):
58
+ def decorator(func):
59
+ self.event_handlers[name] = {
60
+ 'func': func
61
+ }
62
+ def wrapper(*args, **kwargs):
63
+ return func(*args, **kwargs)
64
+
65
+ return wrapper
66
+ return decorator
67
+
68
+
69
+ def client_command(self, name):
70
+ def decorator(func):
71
+ self.client_commands_handlers[name] = {
72
+ 'func': func
73
+ }
74
+ def wrapper(*args, **kwargs):
75
+ return func(*args, **kwargs)
76
+
77
+ return wrapper
78
+ return decorator
79
+
80
+ def timeout(self, label):
81
+ def decorator(func):
82
+ self.timeout_handlers[label] = {
83
+ 'func': func
84
+ }
85
+ def wrapper(*args, **kwargs):
86
+ return func(*args, **kwargs)
87
+
88
+ return wrapper
89
+ return decorator
90
+
91
+ async def eval_client_command(self, command: str, args: list[str], message: Message):
92
+ """Evaluate the client command and call the respective handler"""
93
+ lead = message.lead
94
+ connector = lead.connector
95
+
96
+ if command in self.client_commands_handlers:
97
+ func = self.client_commands_handlers[command]['func']
98
+ # Build args_dict
99
+ from cel.gateway.request_context import RequestContext
100
+ ctx = RequestContext(lead=lead,
101
+ connector=connector,
102
+ assistant=self)
103
+ args_dict = {
104
+ 'session': lead.get_session_id(),
105
+ 'ctx': ctx,
106
+ 'lead': lead,
107
+ 'message': None,
108
+ 'connector': connector,
109
+ 'command': command,
110
+ 'args': args
111
+ }
112
+
113
+ # Build kwargs using args and args_dict
114
+ args = inspect.getfullargspec(func).args
115
+ kwargs = {}
116
+ for arg in args:
117
+ if arg in args_dict:
118
+ kwargs[arg] = args_dict[arg]
119
+
120
+ # Check if the function is a coroutine (async function)
121
+ if inspect.iscoroutinefunction(func):
122
+ await func(**kwargs)
123
+ else:
124
+ func(**kwargs)
125
+ return False
126
+ else:
127
+ log.warning(f"Client command {command} not found in assistant implementation")
128
+ return True
129
+
130
+ async def call_event(self, event_name: str, lead: ConversationLead, message: Message = None, connector: BaseConnector = None, data: dict= None) -> EventResponse:
131
+ """Call the respective event handler, if exists"""
132
+ if event_name in self.event_handlers:
133
+ func = self.event_handlers[event_name]['func']
134
+ connector = connector or lead.connector
135
+ from cel.gateway.request_context import RequestContext
136
+ ctx = RequestContext(lead=lead,
137
+ message=message,
138
+ assistant=self,
139
+ connector=connector)
140
+
141
+ # Build args_dict
142
+ args_dict = {
143
+ 'session': lead.get_session_id(),
144
+ 'ctx': ctx,
145
+ 'lead': lead,
146
+ 'message': message,
147
+ 'connector': connector,
148
+ 'data': data
149
+ }
150
+
151
+ # Build kwargs using args and args_dict
152
+ args = inspect.getfullargspec(func).args
153
+ kwargs = {}
154
+ for arg in args:
155
+ if arg in args_dict:
156
+ kwargs[arg] = args_dict[arg]
157
+
158
+ # Check if the function is a coroutine (async function)
159
+ response = None
160
+ if inspect.iscoroutinefunction(func):
161
+ response = await func(**kwargs)
162
+ else:
163
+ response = func(**kwargs)
164
+
165
+ assert isinstance(response, EventResponse) or response is None, "Event handler must return an instance of EventResponse or None"
166
+ return response
167
+ else:
168
+ # raise ValueError(f"Event {event_name} not found")
169
+ log.error(f"Event {event_name} not found")
170
+
171
+
172
+ async def call_function(self, func_name: str, params: dict, lead: ConversationLead) -> FunctionResponse:
173
+ """Call the respective function handler, if exists. Map function arguments to the function handler signature"""
174
+ connector = lead.connector
175
+ if func_name in self.function_handlers:
176
+ func = self.function_handlers[func_name]['func']
177
+ args = inspect.getfullargspec(func).args
178
+
179
+ ctx = FunctionContext(lead=lead, connector=connector)
180
+ args_dict = {
181
+ 'session': lead.get_session_id(),
182
+ 'ctx': ctx,
183
+ 'params': params,
184
+ 'lead': lead,
185
+ 'message': None,
186
+ 'connector': connector
187
+ }
188
+
189
+ # Build kwargs using args and args_dict
190
+ kwargs = {}
191
+ for arg in args:
192
+ if arg in args_dict:
193
+ kwargs[arg] = args_dict[arg]
194
+
195
+ # Check if the function is a coroutine (async function)
196
+ response = None
197
+ if inspect.iscoroutinefunction(func):
198
+ response = await func(**kwargs)
199
+ else:
200
+ response = func(**kwargs)
201
+
202
+ assert isinstance(response, FunctionResponse) or response is None, "Function handler must return an instance of CommandResponse or None"
203
+ return response
204
+
205
+ else:
206
+ raise ValueError(f"Function {func_name} not found")
207
+
208
+ @abstractmethod
209
+ async def new_message(self, lead: ConversationLead, message: str, local_state: dict = {}):
210
+ raise NotImplementedError
211
+
212
+
213
+ @abstractmethod
214
+ async def blend(self, lead: ConversationLead, text: str, history_length: int = 3):
215
+ raise NotImplementedError
216
+
217
+ @abstractmethod
218
+ async def process_client_command(self, lead: ConversationLead, command: str, args: list[str]):
219
+ yield "Command not found"
220
+
221
+ def get_functions(self) -> list[FunctionDefinition]:
222
+ return [f['definition'] for f in self.function_handlers.values()]
223
+
224
+ def get_events(self):
225
+ return self.event_handlers
226
+
227
+
@@ -0,0 +1,28 @@
1
+
2
+ from abc import ABC
3
+ from dataclasses import dataclass
4
+
5
+
6
+ @dataclass
7
+ class Param(ABC):
8
+ name: str
9
+ type: str
10
+ description: str
11
+ required: bool = True
12
+ enum: list[str] = None
13
+
14
+ @dataclass
15
+ class FunctionDefinition(ABC):
16
+ name: str
17
+ description: str
18
+ parameters: list[Param]
19
+
20
+ @dataclass
21
+ class EventResponse(ABC):
22
+ text: str = None
23
+ image: str = None
24
+ audio: str = None
25
+ video: str = None
26
+ disable_ai_response: bool = False
27
+ blend: bool = False
28
+ is_private: bool = False
@@ -0,0 +1,24 @@
1
+ from cel.assistants.function_response import FunctionResponse, RequestMode
2
+ from cel.gateway.model.base_connector import BaseConnector
3
+ from cel.gateway.model.message import ConversationLead, Message
4
+
5
+
6
+ from dataclasses import dataclass, field
7
+
8
+
9
+ @dataclass
10
+ class FunctionContext:
11
+ lead: ConversationLead
12
+ message: Message | None = None
13
+ connector: BaseConnector | None = None
14
+ state: dict | None = field(default_factory=dict)
15
+ history: list[dict] | None = field(default_factory=list)
16
+
17
+ @staticmethod
18
+ def response_text(text: str, request_mode: RequestMode = RequestMode.SINGLE):
19
+ return FunctionResponse(text=text, request_mode=request_mode)
20
+
21
+
22
+ # @staticmethod
23
+ # def response_image(image_url: str, request_mode: RequestMode = RequestMode.SINGLE):
24
+ # return FunctionResponse(image_url=image_url, request_mode=request_mode)
@@ -0,0 +1,23 @@
1
+ from dataclasses import dataclass
2
+ from typing import Callable
3
+
4
+
5
+ class RequestMode:
6
+ STREAM = "stream"
7
+ SINGLE = "single"
8
+
9
+
10
+ @dataclass
11
+ class FunctionResponse:
12
+ text: str
13
+
14
+ """ the execution function response will be sent as a stream of chunks or as a single response """
15
+ request_mode: str = RequestMode.SINGLE
16
+
17
+ """ If you want to disable direct response from Asiistant AI, use callback to handle the response.
18
+ It is useful when you want to handle the response in a different way, like sending the response to a different service.
19
+ Another use case is when oyu need to use specific platform features to send the response, like interactives messages, buttons, etc.
20
+ """
21
+ callback: Callable = None
22
+
23
+
@@ -0,0 +1,53 @@
1
+ # https://medium.com/@gal.peretz/openrouter-langchain-leverage-opensource-models-without-the-ops-hassle-9ffbf0016da7
2
+
3
+ import os
4
+ from typing import Optional
5
+ from langchain_community.chat_models import ChatOpenAI
6
+ from langchain_core.prompts import ChatPromptTemplate
7
+
8
+
9
+ OPENROUTER_API_BASE = "https://openrouter.ai/api/v1"
10
+
11
+
12
+ # TODO: OpenRouter Models has problems with Langchain tooling
13
+ # This LLM model wrapper throws an error when used with Langchain tooling
14
+ # specifically with bind_tools. The error is due to the fact that the
15
+ # OpenRouter models do not support tooling??? I am not sure about this.
16
+ # --------------------------------------------------------------------
17
+ class ChatOpenRouter(ChatOpenAI):
18
+ openai_api_base: str
19
+ openai_api_key: str
20
+ model_name: str
21
+
22
+ def __init__(self,
23
+ model: str,
24
+ openai_api_key: Optional[str] = None,
25
+ openai_api_base: str = OPENROUTER_API_BASE,
26
+ **kwargs):
27
+ openai_api_key = openai_api_key or os.getenv('OPENROUTER_API_KEY')
28
+ super().__init__(openai_api_base=openai_api_base,
29
+ openai_api_key=openai_api_key,
30
+ model_name=model, **kwargs)
31
+
32
+
33
+ def ChatOpenAIOpenRouter(**kwargs):
34
+ return ChatOpenAI(
35
+ **kwargs,
36
+ openai_api_base=OPENROUTER_API_BASE,
37
+ openai_api_key = os.getenv('OPENROUTER_API_KEY')
38
+ )
39
+ # --------------------------------------------------------------------
40
+
41
+
42
+ # if __name__ == '__main__':
43
+
44
+ # # Load .env variables
45
+ # from dotenv import load_dotenv
46
+ # load_dotenv()
47
+
48
+ # llm = ChatOpenRouter(
49
+ # model="mistralai/mixtral-8x7b-instruct"
50
+ # )
51
+ # prompt = ChatPromptTemplate.from_template("tell me a short joke about {topic}")
52
+ # openrouter_chain = prompt | llm
53
+ # print(openrouter_chain.invoke({"topic": "banana"}))