botversion-sdk 1.0.6__tar.gz → 1.1.6__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 (21) hide show
  1. {botversion_sdk-1.0.6 → botversion_sdk-1.1.6}/PKG-INFO +1 -1
  2. {botversion_sdk-1.0.6 → botversion_sdk-1.1.6}/botversion_sdk/__init__.py +106 -12
  3. botversion_sdk-1.1.6/botversion_sdk/cli/__init__.py +0 -0
  4. botversion_sdk-1.1.6/botversion_sdk/cli/detector.py +1157 -0
  5. botversion_sdk-1.1.6/botversion_sdk/cli/generator.py +169 -0
  6. botversion_sdk-1.1.6/botversion_sdk/cli/init.py +673 -0
  7. botversion_sdk-1.1.6/botversion_sdk/cli/prompts.py +155 -0
  8. botversion_sdk-1.1.6/botversion_sdk/cli/writer.py +724 -0
  9. {botversion_sdk-1.0.6 → botversion_sdk-1.1.6}/botversion_sdk/client.py +16 -8
  10. botversion_sdk-1.1.6/botversion_sdk/interceptor.py +552 -0
  11. {botversion_sdk-1.0.6 → botversion_sdk-1.1.6}/botversion_sdk/scanner.py +659 -32
  12. {botversion_sdk-1.0.6 → botversion_sdk-1.1.6}/botversion_sdk.egg-info/PKG-INFO +1 -1
  13. {botversion_sdk-1.0.6 → botversion_sdk-1.1.6}/botversion_sdk.egg-info/SOURCES.txt +7 -2
  14. {botversion_sdk-1.0.6 → botversion_sdk-1.1.6}/pyproject.toml +1 -1
  15. {botversion_sdk-1.0.6/botversion_sdk → botversion_sdk-1.1.6}/setup.py +1 -1
  16. botversion_sdk-1.0.6/botversion_sdk/interceptor.py +0 -257
  17. botversion_sdk-1.0.6/setup.py +0 -42
  18. {botversion_sdk-1.0.6 → botversion_sdk-1.1.6}/botversion_sdk.egg-info/dependency_links.txt +0 -0
  19. {botversion_sdk-1.0.6 → botversion_sdk-1.1.6}/botversion_sdk.egg-info/entry_points.txt +0 -0
  20. {botversion_sdk-1.0.6 → botversion_sdk-1.1.6}/botversion_sdk.egg-info/top_level.txt +0 -0
  21. {botversion_sdk-1.0.6 → botversion_sdk-1.1.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: botversion-sdk
3
- Version: 1.0.6
3
+ Version: 1.1.6
4
4
  Summary: BotVersion AI Agent SDK for FastAPI, Flask, and Django
5
5
  Home-page: https://github.com/botversion/botversion-sdk-python
6
6
  Author: BotVersion
@@ -10,6 +10,14 @@ from .interceptor import (
10
10
  attach_fastapi_interceptor,
11
11
  attach_flask_interceptor,
12
12
  attach_django_interceptor,
13
+ attach_starlette_interceptor,
14
+ attach_sanic_interceptor,
15
+ attach_falcon_interceptor,
16
+ attach_bottle_interceptor,
17
+ attach_aiohttp_interceptor,
18
+ attach_tornado_interceptor,
19
+ attach_pyramid_interceptor,
20
+ attach_cherrypy_interceptor,
13
21
  )
14
22
 
15
23
  _initialized = False
@@ -55,6 +63,23 @@ def init(app=None, api_key=None, **options):
55
63
  attach_flask_interceptor(app, _client, interceptor_options)
56
64
  elif framework == "django":
57
65
  attach_django_interceptor(_client, interceptor_options)
66
+ # ── NEW ───────────────────────────────────────────────────────
67
+ elif framework == "starlette":
68
+ attach_starlette_interceptor(app, _client, interceptor_options)
69
+ elif framework == "sanic":
70
+ attach_sanic_interceptor(app, _client, interceptor_options)
71
+ elif framework == "falcon":
72
+ attach_falcon_interceptor(app, _client, interceptor_options)
73
+ elif framework == "bottle":
74
+ attach_bottle_interceptor(app, _client, interceptor_options)
75
+ elif framework == "aiohttp":
76
+ attach_aiohttp_interceptor(app, _client, interceptor_options)
77
+ elif framework == "tornado":
78
+ attach_tornado_interceptor(app, _client, interceptor_options)
79
+ elif framework == "pyramid":
80
+ attach_pyramid_interceptor(app, _client, interceptor_options)
81
+ elif framework == "cherrypy":
82
+ attach_cherrypy_interceptor(app, _client, interceptor_options)
58
83
  return
59
84
 
60
85
  _initialized = True
@@ -96,6 +121,23 @@ def init(app=None, api_key=None, **options):
96
121
  attach_flask_interceptor(app, _client, interceptor_options)
97
122
  elif framework == "django":
98
123
  attach_django_interceptor(_client, interceptor_options)
124
+ # ── NEW ───────────────────────────────────────────────────────────────────
125
+ elif framework == "starlette":
126
+ attach_starlette_interceptor(app, _client, interceptor_options)
127
+ elif framework == "sanic":
128
+ attach_sanic_interceptor(app, _client, interceptor_options)
129
+ elif framework == "falcon":
130
+ attach_falcon_interceptor(app, _client, interceptor_options)
131
+ elif framework == "bottle":
132
+ attach_bottle_interceptor(app, _client, interceptor_options)
133
+ elif framework == "aiohttp":
134
+ attach_aiohttp_interceptor(app, _client, interceptor_options)
135
+ elif framework == "tornado":
136
+ attach_tornado_interceptor(app, _client, interceptor_options)
137
+ elif framework == "pyramid":
138
+ attach_pyramid_interceptor(app, _client, interceptor_options)
139
+ elif framework == "cherrypy":
140
+ attach_cherrypy_interceptor(app, _client, interceptor_options)
99
141
  else:
100
142
  return
101
143
 
@@ -110,8 +152,8 @@ def init(app=None, api_key=None, **options):
110
152
  elif framework == "django":
111
153
  endpoints = scan_routes(None, "django")
112
154
 
113
- else:
114
- return
155
+ # For any other framework with no app object, skip backend scan
156
+ # but still continue to frontend scan below
115
157
 
116
158
  if endpoints:
117
159
  _client.register_endpoints_now(endpoints)
@@ -127,17 +169,19 @@ def init(app=None, api_key=None, **options):
127
169
  _client.register_route_patterns(route_patterns)
128
170
 
129
171
  if framework == "flask":
130
- # Scan on startup
131
172
  with app.app_context():
132
- threading.Thread(target=_run_scan, daemon=True).start()
173
+ t = threading.Thread(target=_run_scan, daemon=False)
174
+ t.start()
175
+ t.join(timeout=15)
133
176
  app._botversion_scanned = True
134
177
 
135
- # Keep after_request as fallback for runtime detection
136
178
  @app.after_request
137
179
  def _botversion_first_scan(response):
138
180
  if not getattr(app, '_botversion_scanned', False):
139
181
  app._botversion_scanned = True
140
- threading.Thread(target=_run_scan, daemon=True).start()
182
+ t = threading.Thread(target=_run_scan, daemon=False)
183
+ t.start()
184
+ t.join(timeout=15)
141
185
  return response
142
186
 
143
187
  elif framework == "fastapi":
@@ -145,26 +189,37 @@ def init(app=None, api_key=None, **options):
145
189
  async def _botversion_startup_scan():
146
190
  if not getattr(app, '_botversion_scanned', False):
147
191
  app._botversion_scanned = True
148
- threading.Thread(target=_run_scan, daemon=True).start()
192
+ t = threading.Thread(target=_run_scan, daemon=False)
193
+ t.start()
194
+ t.join(timeout=15)
149
195
 
150
- # Keep middleware as fallback for runtime detection
151
196
  @app.middleware("http")
152
197
  async def _botversion_first_scan(request, call_next):
153
198
  if not getattr(app, '_botversion_scanned', False):
154
199
  app._botversion_scanned = True
155
- threading.Thread(target=_run_scan, daemon=True).start()
200
+ t = threading.Thread(target=_run_scan, daemon=False)
201
+ t.start()
202
+ t.join(timeout=15)
156
203
  return await call_next(request)
157
204
 
158
205
  elif framework == "django":
159
206
  try:
160
207
  from django.apps import apps
161
208
  if apps.ready:
162
- threading.Thread(target=_run_scan, daemon=True).start()
209
+ t = threading.Thread(target=_run_scan, daemon=False)
210
+ t.start()
211
+ t.join(timeout=15)
163
212
  except Exception:
164
- t = threading.Timer(2.0, _run_scan)
165
- t.daemon = True
213
+ t = threading.Timer(3.0, _run_scan)
214
+ t.daemon = False
166
215
  t.start()
167
216
 
217
+ elif framework in ("starlette", "sanic", "falcon", "bottle",
218
+ "aiohttp", "tornado", "pyramid", "cherrypy"):
219
+ t = threading.Timer(3.0, _run_scan)
220
+ t.daemon = False
221
+ t.start()
222
+
168
223
 
169
224
  def get_endpoints():
170
225
  """Get all registered endpoints for this workspace."""
@@ -185,11 +240,32 @@ def register_endpoint(endpoint):
185
240
  def _detect_framework(app):
186
241
  if app is not None:
187
242
  app_type = type(app).__module__ + "." + type(app).__name__
243
+
244
+ # ── Existing ──────────────────────────────────────────────────────
188
245
  if "fastapi" in app_type.lower():
189
246
  return "fastapi"
190
247
  if "flask" in app_type.lower():
191
248
  return "flask"
192
249
 
250
+ # ── NEW ───────────────────────────────────────────────────────────
251
+ if "starlette" in app_type.lower():
252
+ return "starlette"
253
+ if "sanic" in app_type.lower():
254
+ return "sanic"
255
+ if "falcon" in app_type.lower():
256
+ return "falcon"
257
+ if "bottle" in app_type.lower():
258
+ return "bottle"
259
+ if "aiohttp" in app_type.lower():
260
+ return "aiohttp"
261
+ if "tornado" in app_type.lower():
262
+ return "tornado"
263
+ if "pyramid" in app_type.lower():
264
+ return "pyramid"
265
+ if "cherrypy" in app_type.lower():
266
+ return "cherrypy"
267
+
268
+ # ── No app object passed — try detecting from imported modules ────────
193
269
  if app is None:
194
270
  if "django" in sys.modules:
195
271
  try:
@@ -199,11 +275,29 @@ def _detect_framework(app):
199
275
  except Exception:
200
276
  pass
201
277
 
278
+ # ── Fallback — check sys.modules ──────────────────────────────────────
202
279
  if "fastapi" in sys.modules:
203
280
  return "fastapi"
204
281
  if "flask" in sys.modules:
205
282
  return "flask"
206
283
  if "django" in sys.modules:
207
284
  return "django"
285
+ # ── NEW fallbacks ─────────────────────────────────────────────────────
286
+ if "starlette" in sys.modules:
287
+ return "starlette"
288
+ if "sanic" in sys.modules:
289
+ return "sanic"
290
+ if "falcon" in sys.modules:
291
+ return "falcon"
292
+ if "bottle" in sys.modules:
293
+ return "bottle"
294
+ if "aiohttp" in sys.modules:
295
+ return "aiohttp"
296
+ if "tornado" in sys.modules:
297
+ return "tornado"
298
+ if "pyramid" in sys.modules:
299
+ return "pyramid"
300
+ if "cherrypy" in sys.modules:
301
+ return "cherrypy"
208
302
 
209
303
  return None
File without changes