rasa-pro 3.13.1a15__py3-none-any.whl → 3.13.1a17__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.
Potentially problematic release.
This version of rasa-pro might be problematic. Click here for more details.
- rasa/builder/exceptions.py +6 -0
- rasa/builder/inkeep-rag-response-schema.json +64 -0
- rasa/builder/inkeep_document_retrieval.py +212 -0
- rasa/builder/llm_service.py +14 -27
- rasa/builder/models.py +59 -8
- rasa/builder/project_generator.py +3 -2
- rasa/builder/service.py +25 -21
- rasa/core/actions/direct_custom_actions_executor.py +15 -8
- rasa/version.py +1 -1
- {rasa_pro-3.13.1a15.dist-info → rasa_pro-3.13.1a17.dist-info}/METADATA +1 -1
- {rasa_pro-3.13.1a15.dist-info → rasa_pro-3.13.1a17.dist-info}/RECORD +14 -12
- {rasa_pro-3.13.1a15.dist-info → rasa_pro-3.13.1a17.dist-info}/NOTICE +0 -0
- {rasa_pro-3.13.1a15.dist-info → rasa_pro-3.13.1a17.dist-info}/WHEEL +0 -0
- {rasa_pro-3.13.1a15.dist-info → rasa_pro-3.13.1a17.dist-info}/entry_points.txt +0 -0
rasa/builder/exceptions.py
CHANGED
|
@@ -29,6 +29,12 @@ class LLMGenerationError(PromptToBotError):
|
|
|
29
29
|
pass
|
|
30
30
|
|
|
31
31
|
|
|
32
|
+
class DocumentRetrievalError(PromptToBotError):
|
|
33
|
+
"""Raised when document retrieval fails."""
|
|
34
|
+
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
|
|
32
38
|
class SchemaValidationError(PromptToBotError):
|
|
33
39
|
"""Raised when schema validation fails."""
|
|
34
40
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"content": {
|
|
6
|
+
"type": "array",
|
|
7
|
+
"items": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"properties": {
|
|
10
|
+
"type": {
|
|
11
|
+
"type": "string"
|
|
12
|
+
},
|
|
13
|
+
"source": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"properties": {
|
|
16
|
+
"content": {
|
|
17
|
+
"type": "array",
|
|
18
|
+
"items": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"properties": {
|
|
21
|
+
"type": {
|
|
22
|
+
"type": "string"
|
|
23
|
+
},
|
|
24
|
+
"media_type": {
|
|
25
|
+
"type": "string"
|
|
26
|
+
},
|
|
27
|
+
"text": {
|
|
28
|
+
"type": "string"
|
|
29
|
+
},
|
|
30
|
+
"data": {
|
|
31
|
+
"type": "string"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"type": {
|
|
37
|
+
"type": "string"
|
|
38
|
+
},
|
|
39
|
+
"media_type": {
|
|
40
|
+
"type": "string"
|
|
41
|
+
},
|
|
42
|
+
"data": {
|
|
43
|
+
"type": "string"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"title": {
|
|
48
|
+
"type": "string"
|
|
49
|
+
},
|
|
50
|
+
"context": {
|
|
51
|
+
"type": "string"
|
|
52
|
+
},
|
|
53
|
+
"record_type": {
|
|
54
|
+
"type": "string"
|
|
55
|
+
},
|
|
56
|
+
"url": {
|
|
57
|
+
"type": "string"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"required": ["content"]
|
|
64
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
from contextlib import asynccontextmanager
|
|
5
|
+
from typing import List, Optional
|
|
6
|
+
|
|
7
|
+
import importlib_resources
|
|
8
|
+
import openai
|
|
9
|
+
import structlog
|
|
10
|
+
from openai.types.chat import ChatCompletion
|
|
11
|
+
|
|
12
|
+
from rasa.builder.exceptions import DocumentRetrievalError
|
|
13
|
+
from rasa.builder.models import Document
|
|
14
|
+
from rasa.constants import PACKAGE_NAME
|
|
15
|
+
from rasa.shared.utils.io import read_json_file
|
|
16
|
+
|
|
17
|
+
INKEEP_API_KEY_ENV_VAR = "INKEEP_API_KEY"
|
|
18
|
+
INKEEP_RAG_RESPONSE_SCHEMA_PATH = str(
|
|
19
|
+
importlib_resources.files(PACKAGE_NAME).joinpath(
|
|
20
|
+
"builder/inkeep-rag-response-schema.json"
|
|
21
|
+
)
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
INKEEP_DOCUMENT_RETRIEVAL_MODEL = "inkeep-rag"
|
|
25
|
+
INKEEP_BASE_URL = "https://api.inkeep.com/v1/"
|
|
26
|
+
|
|
27
|
+
structlogger = structlog.get_logger()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class InKeepDocumentRetrieval:
|
|
31
|
+
"""Handles the document retrieval from InKeep AI."""
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
api_key: Optional[str] = None,
|
|
36
|
+
):
|
|
37
|
+
self._client: Optional[openai.AsyncOpenAI] = None
|
|
38
|
+
self._rag_schema = read_json_file(INKEEP_RAG_RESPONSE_SCHEMA_PATH)
|
|
39
|
+
self._api_key = api_key or os.getenv(INKEEP_API_KEY_ENV_VAR)
|
|
40
|
+
|
|
41
|
+
async def retrieve_documents(
|
|
42
|
+
self, query: str, temperature: float = 0.0, timeout: float = 30.0
|
|
43
|
+
) -> List[Document]:
|
|
44
|
+
"""Retrieve relevant documents using InKeep AI based on the given query.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
query: The search query
|
|
48
|
+
temperature: Controls randomness in generation (0.0 for deterministic)
|
|
49
|
+
timeout: Timeout for the API call
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
List of Document objects containing retrieved content
|
|
53
|
+
|
|
54
|
+
Raises:
|
|
55
|
+
LLMGenerationError: If the API call fails or returns invalid response
|
|
56
|
+
"""
|
|
57
|
+
response = await self._call_inkeep_rag_api(
|
|
58
|
+
query=query,
|
|
59
|
+
temperature=temperature,
|
|
60
|
+
timeout=timeout,
|
|
61
|
+
)
|
|
62
|
+
documents = self._parse_documents_from_response(response)
|
|
63
|
+
return documents
|
|
64
|
+
|
|
65
|
+
async def _call_inkeep_rag_api(
|
|
66
|
+
self, query: str, temperature: float, timeout: float
|
|
67
|
+
) -> ChatCompletion:
|
|
68
|
+
"""Call InKeep AI RAG's API endpoint and return the response content.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
query: The search query to send to InKeep
|
|
72
|
+
temperature: Controls randomness in generation (0.0 for deterministic)
|
|
73
|
+
timeout: Timeout for the API call
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
The response content from InKeep AI. The response is made of the retrieved
|
|
77
|
+
documents.
|
|
78
|
+
|
|
79
|
+
Raises:
|
|
80
|
+
LLMGenerationError: If the API call fails or returns invalid response
|
|
81
|
+
"""
|
|
82
|
+
request_params = {
|
|
83
|
+
"model": INKEEP_DOCUMENT_RETRIEVAL_MODEL,
|
|
84
|
+
"messages": [{"role": "user", "content": query}],
|
|
85
|
+
"temperature": temperature,
|
|
86
|
+
"timeout": timeout,
|
|
87
|
+
"response_format": {
|
|
88
|
+
"type": "json_schema",
|
|
89
|
+
"json_schema": self._rag_schema,
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
try:
|
|
93
|
+
async with self._get_client() as client:
|
|
94
|
+
response = await client.chat.completions.create(**request_params)
|
|
95
|
+
|
|
96
|
+
if not response.choices[0].message.content:
|
|
97
|
+
structlogger.warning(
|
|
98
|
+
"inkeep_document_retrieval.empty_response",
|
|
99
|
+
event_info="InKeep AI returned an empty response. ",
|
|
100
|
+
request_params=request_params,
|
|
101
|
+
response_content=response.choices[0].message.content,
|
|
102
|
+
)
|
|
103
|
+
raise DocumentRetrievalError(
|
|
104
|
+
"InKeep Document Retrieval: Empty response"
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
return response
|
|
108
|
+
|
|
109
|
+
except openai.OpenAIError as e:
|
|
110
|
+
structlogger.error(
|
|
111
|
+
"inkeep_document_retrieval.api_error",
|
|
112
|
+
event_info="InKeep Document Retrieval: API error",
|
|
113
|
+
request_params=request_params,
|
|
114
|
+
error=e,
|
|
115
|
+
)
|
|
116
|
+
raise DocumentRetrievalError(f"InKeep Document Retrieval: API error: {e}")
|
|
117
|
+
except asyncio.TimeoutError as e:
|
|
118
|
+
structlogger.error(
|
|
119
|
+
"inkeep_document_retrieval.timeout_error",
|
|
120
|
+
event_info="InKeep Document Retrieval: Timeout error",
|
|
121
|
+
request_params=request_params,
|
|
122
|
+
error=e,
|
|
123
|
+
)
|
|
124
|
+
raise DocumentRetrievalError(f"InKeep AI request timed out: {e}")
|
|
125
|
+
except Exception as e:
|
|
126
|
+
structlogger.error(
|
|
127
|
+
"inkeep_document_retrieval.error",
|
|
128
|
+
event_info="InKeep Document Retrieval: Error",
|
|
129
|
+
request_params=request_params,
|
|
130
|
+
error=e,
|
|
131
|
+
)
|
|
132
|
+
raise DocumentRetrievalError(
|
|
133
|
+
f"InKeep Document Retrieval: Unexpected error: {e}"
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
@asynccontextmanager
|
|
137
|
+
async def _get_client(self):
|
|
138
|
+
"""Get or create client that handles the API calls to InKeep AI."""
|
|
139
|
+
if self._client is None:
|
|
140
|
+
self._client = openai.AsyncOpenAI(
|
|
141
|
+
api_key=self._api_key,
|
|
142
|
+
base_url=INKEEP_BASE_URL,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
try:
|
|
146
|
+
yield self._client
|
|
147
|
+
except Exception as e:
|
|
148
|
+
structlogger.error(
|
|
149
|
+
"inkeep_document_retrieval.client_error",
|
|
150
|
+
event_info="InKeep Document Retrieval: Client error",
|
|
151
|
+
error=str(e),
|
|
152
|
+
)
|
|
153
|
+
raise
|
|
154
|
+
|
|
155
|
+
def _parse_documents_from_response(
|
|
156
|
+
self, response: ChatCompletion
|
|
157
|
+
) -> List[Document]:
|
|
158
|
+
"""Parse the InKeep AI response into Document objects.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
response: ChatCompletion response from InKeep AI's RAG model.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
List of Document objects
|
|
165
|
+
"""
|
|
166
|
+
try:
|
|
167
|
+
content = response.choices[0].message.content
|
|
168
|
+
if not content:
|
|
169
|
+
return []
|
|
170
|
+
|
|
171
|
+
response_data = json.loads(content)
|
|
172
|
+
documents = []
|
|
173
|
+
|
|
174
|
+
for item in response_data.get("content", []):
|
|
175
|
+
try:
|
|
176
|
+
document = Document.from_inkeep_rag_response(item)
|
|
177
|
+
documents.append(document)
|
|
178
|
+
except Exception as e:
|
|
179
|
+
structlogger.warning(
|
|
180
|
+
"inkeep_document_retrieval.invalid_document_skipped",
|
|
181
|
+
event_info=(
|
|
182
|
+
"InKeep Document Retrieval: Invalid document structure "
|
|
183
|
+
"skipped. Returning empty list for this item."
|
|
184
|
+
),
|
|
185
|
+
error=str(e),
|
|
186
|
+
item=item,
|
|
187
|
+
)
|
|
188
|
+
# Continue processing other items, skip this invalid one
|
|
189
|
+
continue
|
|
190
|
+
|
|
191
|
+
return documents
|
|
192
|
+
|
|
193
|
+
except json.JSONDecodeError as e:
|
|
194
|
+
structlogger.warning(
|
|
195
|
+
"inkeep_document_retrieval.parse_response_failed",
|
|
196
|
+
event_info=(
|
|
197
|
+
"InKeep Document Retrieval: Parse response failed. "
|
|
198
|
+
"Returning empty list.",
|
|
199
|
+
),
|
|
200
|
+
error=str(e),
|
|
201
|
+
)
|
|
202
|
+
return []
|
|
203
|
+
except Exception as e:
|
|
204
|
+
structlogger.error(
|
|
205
|
+
"inkeep_document_retrieval.parse_response_error",
|
|
206
|
+
event_info=(
|
|
207
|
+
"InKeep Document Retrieval: Parse response error. "
|
|
208
|
+
"Returning empty list.",
|
|
209
|
+
),
|
|
210
|
+
error=str(e),
|
|
211
|
+
)
|
|
212
|
+
return []
|
rasa/builder/llm_service.py
CHANGED
|
@@ -11,11 +11,13 @@ import importlib_resources
|
|
|
11
11
|
import openai
|
|
12
12
|
import structlog
|
|
13
13
|
from jinja2 import Template
|
|
14
|
+
from pydantic import ValidationError
|
|
14
15
|
|
|
15
16
|
from rasa.builder import config
|
|
16
17
|
from rasa.builder.exceptions import LLMGenerationError
|
|
18
|
+
from rasa.builder.inkeep_document_retrieval import InKeepDocumentRetrieval
|
|
17
19
|
from rasa.builder.llm_context import tracker_as_llm_context
|
|
18
|
-
from rasa.builder.models import LLMBuilderContext
|
|
20
|
+
from rasa.builder.models import Document, LLMBuilderContext, LLMHelperResponse
|
|
19
21
|
from rasa.constants import PACKAGE_NAME
|
|
20
22
|
from rasa.shared.constants import DOMAIN_SCHEMA_FILE, RESPONSES_SCHEMA_FILE
|
|
21
23
|
from rasa.shared.core.flows.yaml_flows_io import FLOWS_SCHEMA_FILE
|
|
@@ -135,7 +137,7 @@ class LLMService:
|
|
|
135
137
|
|
|
136
138
|
async def generate_helper_response(
|
|
137
139
|
self, messages: List[Dict[str, Any]]
|
|
138
|
-
) ->
|
|
140
|
+
) -> LLMHelperResponse:
|
|
139
141
|
"""Generate helper response using OpenAI."""
|
|
140
142
|
self._prepare_schemas()
|
|
141
143
|
|
|
@@ -158,9 +160,11 @@ class LLMService:
|
|
|
158
160
|
raise LLMGenerationError("Empty response from LLM helper")
|
|
159
161
|
|
|
160
162
|
try:
|
|
161
|
-
return json.loads(content)
|
|
163
|
+
return LLMHelperResponse.model_validate_json(json.loads(content))
|
|
162
164
|
except json.JSONDecodeError as e:
|
|
163
165
|
raise LLMGenerationError(f"Invalid JSON from LLM helper: {e}")
|
|
166
|
+
except ValidationError as e:
|
|
167
|
+
raise LLMGenerationError(f"Invalid JSON from LLM helper: {e}")
|
|
164
168
|
|
|
165
169
|
except openai.OpenAIError as e:
|
|
166
170
|
raise LLMGenerationError(f"OpenAI API error in helper: {e}")
|
|
@@ -169,27 +173,11 @@ class LLMService:
|
|
|
169
173
|
|
|
170
174
|
async def search_documentation(
|
|
171
175
|
self, query: str, max_results: Optional[int] = None
|
|
172
|
-
) -> List[
|
|
176
|
+
) -> List[Document]:
|
|
173
177
|
"""Search documentation using OpenAI vector store."""
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
try:
|
|
178
|
-
async with self._get_client() as client:
|
|
179
|
-
results = await client.vector_stores.search(
|
|
180
|
-
vector_store_id=config.OPENAI_VECTOR_STORE_ID,
|
|
181
|
-
query=query,
|
|
182
|
-
max_num_results=max_results,
|
|
183
|
-
rewrite_query=True,
|
|
184
|
-
)
|
|
185
|
-
|
|
186
|
-
return results.data
|
|
187
|
-
|
|
188
|
-
except openai.OpenAIError as e:
|
|
189
|
-
structlogger.warning(
|
|
190
|
-
"llm.documentation_search_failed", error=str(e), query=query
|
|
191
|
-
)
|
|
192
|
-
return []
|
|
178
|
+
inkeep_document_retrieval = InKeepDocumentRetrieval()
|
|
179
|
+
documents = await inkeep_document_retrieval.retrieve_documents(query)
|
|
180
|
+
return documents
|
|
193
181
|
|
|
194
182
|
@staticmethod
|
|
195
183
|
def _format_chat_dump(messages: List[Dict[str, Any]]) -> str:
|
|
@@ -213,16 +201,15 @@ class LLMService:
|
|
|
213
201
|
return result
|
|
214
202
|
|
|
215
203
|
@staticmethod
|
|
216
|
-
def _format_documentation_results(results: List[
|
|
204
|
+
def _format_documentation_results(results: List[Document]) -> str:
|
|
217
205
|
"""Format documentation search results."""
|
|
218
206
|
if not results:
|
|
219
207
|
return "<sources>No relevant documentation found.</sources>"
|
|
220
208
|
|
|
221
209
|
formatted_results = ""
|
|
222
210
|
for result in results:
|
|
223
|
-
formatted_result = f"<result url='{result.
|
|
224
|
-
|
|
225
|
-
formatted_result += f"<content>{part.text}</content>"
|
|
211
|
+
formatted_result = f"<result url='{result.url}'>"
|
|
212
|
+
formatted_result += f"<content>{result.content}</content>"
|
|
226
213
|
formatted_results += formatted_result + "</result>"
|
|
227
214
|
|
|
228
215
|
return f"<sources>{formatted_results}</sources>"
|
rasa/builder/models.py
CHANGED
|
@@ -129,14 +129,6 @@ class LLMHelperResponse(BaseModel):
|
|
|
129
129
|
content_blocks: List[Union[TextBlock, CodeBlock, FileBlock, LinkBlock]] = Field(...)
|
|
130
130
|
|
|
131
131
|
|
|
132
|
-
class ApiResponse(BaseModel):
|
|
133
|
-
"""Standard API response model."""
|
|
134
|
-
|
|
135
|
-
status: str = Field(...)
|
|
136
|
-
message: Optional[str] = Field(None)
|
|
137
|
-
data: Optional[Dict[str, Any]] = Field(None)
|
|
138
|
-
|
|
139
|
-
|
|
140
132
|
class ApiErrorResponse(BaseModel):
|
|
141
133
|
"""API error response model."""
|
|
142
134
|
|
|
@@ -172,3 +164,62 @@ class TrainingResult(BaseModel):
|
|
|
172
164
|
success: bool = Field(...)
|
|
173
165
|
model_path: Optional[str] = Field(None)
|
|
174
166
|
error: Optional[str] = Field(None)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
BotFiles = Dict[str, Optional[str]]
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class Document(BaseModel):
|
|
173
|
+
"""Model for document retrieval results."""
|
|
174
|
+
|
|
175
|
+
content: str = Field(...)
|
|
176
|
+
url: Optional[str] = Field(None)
|
|
177
|
+
title: Optional[str] = Field(None)
|
|
178
|
+
metadata: Optional[Dict[str, Any]] = Field(None)
|
|
179
|
+
|
|
180
|
+
@classmethod
|
|
181
|
+
def from_inkeep_rag_response(cls, rag_item: Dict[str, Any]) -> "Document":
|
|
182
|
+
"""Create a Document object from a single InKeep RAG response item.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
rag_item: Single item from InKeep RAG response
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
Document object with extracted content and metadata
|
|
189
|
+
"""
|
|
190
|
+
source = rag_item.get("source", {})
|
|
191
|
+
text_content = cls._extract_text_from_source(source)
|
|
192
|
+
|
|
193
|
+
return cls(
|
|
194
|
+
content=text_content.strip() if text_content else "",
|
|
195
|
+
url=rag_item.get("url"),
|
|
196
|
+
title=rag_item.get("title"),
|
|
197
|
+
metadata={
|
|
198
|
+
"type": rag_item.get("type"),
|
|
199
|
+
"record_type": rag_item.get("record_type"),
|
|
200
|
+
"context": rag_item.get("context"),
|
|
201
|
+
"media_type": source.get("media_type"),
|
|
202
|
+
},
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
@staticmethod
|
|
206
|
+
def _extract_text_from_source(source: Dict[str, Any]) -> str:
|
|
207
|
+
"""Extract text content from InKeep source object.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
source: Source object from InKeep RAG response
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
Extracted text content
|
|
214
|
+
"""
|
|
215
|
+
# Try to extract from content array first
|
|
216
|
+
if "content" in source:
|
|
217
|
+
text_parts = []
|
|
218
|
+
for content_item in source["content"]:
|
|
219
|
+
if content_item.get("type") == "text" and content_item.get("text"):
|
|
220
|
+
text_parts.append(content_item["text"])
|
|
221
|
+
if text_parts:
|
|
222
|
+
return "\n".join(text_parts)
|
|
223
|
+
|
|
224
|
+
# Fallback to source data
|
|
225
|
+
return source.get("data", "")
|
|
@@ -12,6 +12,7 @@ import structlog
|
|
|
12
12
|
from rasa.builder import config
|
|
13
13
|
from rasa.builder.exceptions import ProjectGenerationError, ValidationError
|
|
14
14
|
from rasa.builder.llm_service import get_skill_generation_messages, llm_service
|
|
15
|
+
from rasa.builder.models import BotFiles
|
|
15
16
|
from rasa.builder.validation_service import validate_project
|
|
16
17
|
from rasa.cli.scaffold import ProjectTemplateName, create_initial_project
|
|
17
18
|
from rasa.shared.core.flows import yaml_flows_io
|
|
@@ -178,9 +179,9 @@ class ProjectGenerator:
|
|
|
178
179
|
except Exception as e:
|
|
179
180
|
raise ValidationError(f"Failed to create importer: {e}")
|
|
180
181
|
|
|
181
|
-
def get_bot_files(self) ->
|
|
182
|
+
def get_bot_files(self) -> BotFiles:
|
|
182
183
|
"""Get the current bot files by reading from disk."""
|
|
183
|
-
bot_files:
|
|
184
|
+
bot_files: BotFiles = {}
|
|
184
185
|
|
|
185
186
|
for file in self.project_folder.glob("**/*"):
|
|
186
187
|
# Skip directories
|
rasa/builder/service.py
CHANGED
|
@@ -18,9 +18,9 @@ from rasa.builder.llm_service import llm_service
|
|
|
18
18
|
from rasa.builder.logging_utils import get_recent_logs
|
|
19
19
|
from rasa.builder.models import (
|
|
20
20
|
ApiErrorResponse,
|
|
21
|
-
ApiResponse,
|
|
22
21
|
LLMBuilderContext,
|
|
23
22
|
LLMBuilderRequest,
|
|
23
|
+
LLMHelperResponse,
|
|
24
24
|
PromptRequest,
|
|
25
25
|
ServerSentEvent,
|
|
26
26
|
TemplateRequest,
|
|
@@ -449,7 +449,7 @@ async def handle_template_to_bot(request: Request) -> None:
|
|
|
449
449
|
@openapi.tag("bot-files")
|
|
450
450
|
@openapi.response(
|
|
451
451
|
200,
|
|
452
|
-
{"application/json":
|
|
452
|
+
{"application/json": {str: Optional[str]}},
|
|
453
453
|
description="Bot files retrieved successfully",
|
|
454
454
|
)
|
|
455
455
|
@openapi.response(
|
|
@@ -461,25 +461,35 @@ async def get_bot_files(request: Request) -> HTTPResponse:
|
|
|
461
461
|
"""Get current bot files."""
|
|
462
462
|
project_generator = get_project_generator(request)
|
|
463
463
|
bot_files = project_generator.get_bot_files()
|
|
464
|
-
return response.json(
|
|
465
|
-
ApiResponse(
|
|
466
|
-
status="success",
|
|
467
|
-
message="Bot files fetched successfully",
|
|
468
|
-
data={"files": bot_files},
|
|
469
|
-
).model_dump()
|
|
470
|
-
)
|
|
464
|
+
return response.json(bot_files)
|
|
471
465
|
|
|
472
466
|
|
|
473
467
|
@bp.route("/files", methods=["PUT"])
|
|
474
468
|
@openapi.summary("Update bot files")
|
|
475
469
|
@openapi.description(
|
|
476
470
|
"Updates the bot configuration files and retrains the model. "
|
|
477
|
-
"Returns server-sent events for real-time progress tracking
|
|
471
|
+
"Returns server-sent events (SSE) for real-time progress tracking "
|
|
472
|
+
"through the entire update process.\n\n"
|
|
473
|
+
"**SSE Event Flow:**\n"
|
|
474
|
+
"1. `received` - Request received by server\n"
|
|
475
|
+
"2. `validating` - Validating bot configuration files\n"
|
|
476
|
+
"3. `validation_success` - File validation completed successfully\n"
|
|
477
|
+
"4. `training` - Training the bot model with updated files\n"
|
|
478
|
+
"5. `train_success` - Model training completed\n"
|
|
479
|
+
"6. `done` - Bot files update completed\n\n"
|
|
480
|
+
"**Error Events (can occur at any time):**\n"
|
|
481
|
+
"- `validation_error` - Bot configuration files are invalid\n"
|
|
482
|
+
"- `train_error` - Files updated but training failed\n"
|
|
483
|
+
"- `error` - Unexpected error occurred\n\n"
|
|
484
|
+
"**Usage:** Send PUT request with Content-Type: application/json and "
|
|
485
|
+
"Accept: text/event-stream"
|
|
478
486
|
)
|
|
479
487
|
@openapi.tag("bot-files")
|
|
480
488
|
@openapi.body(
|
|
481
|
-
{"application/json":
|
|
482
|
-
description="
|
|
489
|
+
{"application/json": {"file_name": str}},
|
|
490
|
+
description="A dictionary mapping file names to their updated content. "
|
|
491
|
+
"The file name should be the name of the file in the project folder. "
|
|
492
|
+
"Files that are not in the request will not be updated.",
|
|
483
493
|
required=True,
|
|
484
494
|
)
|
|
485
495
|
@openapi.response(
|
|
@@ -603,7 +613,7 @@ async def update_bot_files(request: Request) -> None:
|
|
|
603
613
|
@openapi.tag("bot-data")
|
|
604
614
|
@openapi.response(
|
|
605
615
|
200,
|
|
606
|
-
{"application/json": model_to_schema(
|
|
616
|
+
{"application/json": model_to_schema(CALMUserData)},
|
|
607
617
|
description="Bot data retrieved successfully",
|
|
608
618
|
)
|
|
609
619
|
@openapi.response(
|
|
@@ -617,13 +627,7 @@ async def get_bot_data(request: Request) -> HTTPResponse:
|
|
|
617
627
|
project_generator = get_project_generator(request)
|
|
618
628
|
calm_parts = extract_calm_import_parts_from_project_generator(project_generator)
|
|
619
629
|
|
|
620
|
-
return response.json(
|
|
621
|
-
ApiResponse(
|
|
622
|
-
status="success",
|
|
623
|
-
message="Bot data fetched successfully",
|
|
624
|
-
data=calm_parts.model_dump(),
|
|
625
|
-
).model_dump()
|
|
626
|
-
)
|
|
630
|
+
return response.json(calm_parts.model_dump())
|
|
627
631
|
except Exception as e:
|
|
628
632
|
structlogger.error("bot_builder_service.get_bot_data.error", error=str(e))
|
|
629
633
|
return response.json(
|
|
@@ -649,7 +653,7 @@ async def get_bot_data(request: Request) -> HTTPResponse:
|
|
|
649
653
|
)
|
|
650
654
|
@openapi.response(
|
|
651
655
|
200,
|
|
652
|
-
{"application/json":
|
|
656
|
+
{"application/json": model_to_schema(LLMHelperResponse)},
|
|
653
657
|
description="LLM response with assistance and suggestions",
|
|
654
658
|
)
|
|
655
659
|
@openapi.response(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import os
|
|
1
2
|
from functools import lru_cache
|
|
2
|
-
from importlib.util import find_spec
|
|
3
3
|
from typing import (
|
|
4
4
|
Any,
|
|
5
5
|
ClassVar,
|
|
@@ -16,7 +16,6 @@ from rasa.core.actions.custom_action_executor import (
|
|
|
16
16
|
)
|
|
17
17
|
from rasa.shared.core.domain import Domain
|
|
18
18
|
from rasa.shared.core.trackers import DialogueStateTracker, EventVerbosity
|
|
19
|
-
from rasa.shared.exceptions import RasaException
|
|
20
19
|
from rasa.utils.endpoints import EndpointConfig
|
|
21
20
|
|
|
22
21
|
structlogger = structlog.get_logger(__name__)
|
|
@@ -63,12 +62,20 @@ class DirectCustomActionExecutor(CustomActionExecutor):
|
|
|
63
62
|
return
|
|
64
63
|
|
|
65
64
|
module_name = self.action_endpoint.actions_module
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
)
|
|
65
|
+
# TODO: remove this / change back
|
|
66
|
+
structlogger.info(
|
|
67
|
+
"action.direct_custom_action_executor.register_actions_from_a_module",
|
|
68
|
+
module_name=module_name,
|
|
69
|
+
cwd=os.getcwd(),
|
|
70
|
+
ls_dir=os.listdir(os.getcwd()),
|
|
71
|
+
ls_dir_module=os.listdir(os.getcwd() + "/" + module_name),
|
|
72
|
+
)
|
|
73
|
+
# if not find_spec(module_name):
|
|
74
|
+
# raise RasaException(
|
|
75
|
+
# f"You've provided the custom actions module '{module_name}' "
|
|
76
|
+
# f"to run directly by the rasa server, however this module does "
|
|
77
|
+
# f"not exist. Please check for typos in your `endpoints.yml` file."
|
|
78
|
+
# )
|
|
72
79
|
|
|
73
80
|
self.action_executor.register_package(module_name)
|
|
74
81
|
DirectCustomActionExecutor._actions_module_registered = True
|
rasa/version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: rasa-pro
|
|
3
|
-
Version: 3.13.
|
|
3
|
+
Version: 3.13.1a17
|
|
4
4
|
Summary: State-of-the-art open-core Conversational AI framework for Enterprises that natively leverages generative AI for effortless assistant development.
|
|
5
5
|
Keywords: nlp,machine-learning,machine-learning-library,bot,bots,botkit,rasa conversational-agents,conversational-ai,chatbot,chatbot-framework,bot-framework
|
|
6
6
|
Author: Rasa Technologies GmbH
|
|
@@ -5,17 +5,19 @@ rasa/builder/README.md,sha256=7WYioSzBHFY25h1QCFellv7bIOW9VLH7Gf7dwQEc1k0,3715
|
|
|
5
5
|
rasa/builder/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
6
|
rasa/builder/config.py,sha256=NxTvyqWGzJnPANv2s78SBv98fiVhebF2ktzWgr6W0CA,2461
|
|
7
7
|
rasa/builder/create_openai_vector_store.py,sha256=jAk1QzM4HiC0wjkn1031xBzLFGwVV4JUJMc50QZFIdw,6642
|
|
8
|
-
rasa/builder/exceptions.py,sha256=
|
|
8
|
+
rasa/builder/exceptions.py,sha256=lsMX_892AoDi4yY86mIX0x-hGvCDZN22kOTTGM5Tvro,1220
|
|
9
|
+
rasa/builder/inkeep-rag-response-schema.json,sha256=ePVbGo6u6sm_CPS8EeECX3UayIvcfUe6yTy2gtexKlk,1498
|
|
10
|
+
rasa/builder/inkeep_document_retrieval.py,sha256=FafqkRgwn-OktF2oBEUzGw28KhmKPU90SSL5Tb8jLZA,7467
|
|
9
11
|
rasa/builder/llm-helper-schema.json,sha256=z5IJc_-2mZ9oQ-z-9WzTivOoqYsLXCAm8MIOTWy5rGs,1609
|
|
10
12
|
rasa/builder/llm_context.py,sha256=zy7htrXgS_QWJWeEj4TfseQgTI65whFJR_4GKm_iOvE,2826
|
|
11
13
|
rasa/builder/llm_helper_prompt.jinja2,sha256=AhfEzXYIMTmWgd2TgVmPVeCfojHA29IiuO6JhTOXXKY,9585
|
|
12
|
-
rasa/builder/llm_service.py,sha256=
|
|
14
|
+
rasa/builder/llm_service.py,sha256=vAVekor9ZBRZw6-TGDGXoutd9UaNnQV2GltbLbM0-Qc,11459
|
|
13
15
|
rasa/builder/logging_utils.py,sha256=iPJoN2HhNlS14SKyZv0s0iIljrmP6A8s8C5btoDVOXM,1383
|
|
14
16
|
rasa/builder/main.py,sha256=KzlVDESi5FO3ZAXxr2RISwn2aXACaY4jjZ5Fb-lcVM4,4242
|
|
15
|
-
rasa/builder/models.py,sha256=
|
|
16
|
-
rasa/builder/project_generator.py,sha256=
|
|
17
|
+
rasa/builder/models.py,sha256=EuP6FVD0VIkG8Jzf8ftYW2A1xirDRTlN4Ae-OmdLdAA,6208
|
|
18
|
+
rasa/builder/project_generator.py,sha256=7z2wyiwXFYQdw9RT2Yht5NrrtWwntgTBdCuaJGrEheM,10937
|
|
17
19
|
rasa/builder/scrape_rasa_docs.py,sha256=yWezvPgfLkokm9t71ngyckNKCiY4bRvc9azEgSYN0Sg,2480
|
|
18
|
-
rasa/builder/service.py,sha256=
|
|
20
|
+
rasa/builder/service.py,sha256=Z8yvRCnRYQbdHtyiNXLU8LhLlOl7FvccXbpWbOJ0RVU,24575
|
|
19
21
|
rasa/builder/skill_to_bot_prompt.jinja2,sha256=h2Fgoh9k3XinN0blEEqMuOWuvwXxJifP3GJs-GczgBU,5530
|
|
20
22
|
rasa/builder/training_service.py,sha256=9OIRZ6CRXHc_m9VHkeHHK7woN5Q1n7BpmsxjYjuByoI,4046
|
|
21
23
|
rasa/builder/validation_service.py,sha256=rKMgbG8Jyv8WMnTIXOMd7VuGWAYicrL9wDJ22BJXZHE,2765
|
|
@@ -308,7 +310,7 @@ rasa/core/actions/action_trigger_flow.py,sha256=IydYAGafTtoY6XSgCX124xJQhzudUg8J
|
|
|
308
310
|
rasa/core/actions/action_trigger_search.py,sha256=QfYqnaGRCqRYJ4msYsLAbnVYW5ija_tqhCcKIN8aEfw,1064
|
|
309
311
|
rasa/core/actions/constants.py,sha256=gfgdWmj-OJ5xTcTAS1OcXQ3dgcTiHO98NC-SGyKlTjs,161
|
|
310
312
|
rasa/core/actions/custom_action_executor.py,sha256=qafASBdM3-hByDqbkNxgXfx5yMSsJh_nB3B7x9ye0TY,6176
|
|
311
|
-
rasa/core/actions/direct_custom_actions_executor.py,sha256=
|
|
313
|
+
rasa/core/actions/direct_custom_actions_executor.py,sha256=JXlyRA40Hedkz6URyemUEIV0fFCehoOoAzvatu--2FU,4025
|
|
312
314
|
rasa/core/actions/e2e_stub_custom_action_executor.py,sha256=D-kECC1QjVLv4owNxstW2xJPPsXTGfGepvquMeWB_ec,2282
|
|
313
315
|
rasa/core/actions/forms.py,sha256=MPGxp3vg-EgFcU5UQYqWM2tycSFIuoF6vWvNSSWPhSA,26967
|
|
314
316
|
rasa/core/actions/grpc_custom_action_executor.py,sha256=EDxdSIDA4H4Mu-QZk-pPGV2N41ZsbY8W9laV6l1WlDQ,9103
|
|
@@ -1066,9 +1068,9 @@ rasa/utils/train_utils.py,sha256=ClJx-6x3-h3Vt6mskacgkcCUJTMXjFPe3zAcy_DfmaU,212
|
|
|
1066
1068
|
rasa/utils/url_tools.py,sha256=dZ1HGkVdWTJB7zYEdwoDIrEuyX9HE5WsxKKFVsXBLE0,1218
|
|
1067
1069
|
rasa/utils/yaml.py,sha256=KjbZq5C94ZP7Jdsw8bYYF7HASI6K4-C_kdHfrnPLpSI,2000
|
|
1068
1070
|
rasa/validator.py,sha256=IRhLfcgCpps0wSpokOvUGNaY8t8GsmeSmPOUVRKeOeE,83087
|
|
1069
|
-
rasa/version.py,sha256=
|
|
1070
|
-
rasa_pro-3.13.
|
|
1071
|
-
rasa_pro-3.13.
|
|
1072
|
-
rasa_pro-3.13.
|
|
1073
|
-
rasa_pro-3.13.
|
|
1074
|
-
rasa_pro-3.13.
|
|
1071
|
+
rasa/version.py,sha256=AVviI7uhVQCuE-dl34eiA4si4yQj06yJK9V2U7CyeeM,120
|
|
1072
|
+
rasa_pro-3.13.1a17.dist-info/METADATA,sha256=3NmO_L2ac2WjOweNuV5fYTtIKciTj_ezAyC8nIwCmLI,10610
|
|
1073
|
+
rasa_pro-3.13.1a17.dist-info/NOTICE,sha256=7HlBoMHJY9CL2GlYSfTQ-PZsVmLmVkYmMiPlTjhuCqA,218
|
|
1074
|
+
rasa_pro-3.13.1a17.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
|
1075
|
+
rasa_pro-3.13.1a17.dist-info/entry_points.txt,sha256=ckJ2SfEyTPgBqj_I6vm_tqY9dZF_LAPJZA335Xp0Q9U,43
|
|
1076
|
+
rasa_pro-3.13.1a17.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|