osmosis-ai 0.1.7__py3-none-any.whl → 0.1.9__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 osmosis-ai might be problematic. Click here for more details.

@@ -1,502 +0,0 @@
1
- """
2
- Anthropic adapter for Osmosis
3
-
4
- This module provides monkey patching for the Anthropic Python client.
5
- """
6
-
7
- import functools
8
- import sys
9
-
10
- from osmosis_ai.utils import send_to_osmosis
11
- from osmosis_ai import utils
12
- from osmosis_ai.logger import logger
13
-
14
-
15
- def wrap_anthropic() -> None:
16
- """
17
- Monkey patch Anthropic's client to send all prompts and responses to OSMOSIS.
18
-
19
- This function should be called before creating any Anthropic client instances.
20
-
21
- Features supported:
22
- - Basic message completions
23
- - Async message completions
24
- - Tool use (function calling)
25
- - Tool responses
26
- - Streaming (if available)
27
- """
28
- try:
29
- import anthropic
30
- except ImportError:
31
- logger.debug("anthropic package is not installed.")
32
- return
33
-
34
- logger.info(f"Wrapping Anthropic client, package version: {anthropic.__version__}")
35
-
36
- # Check which version of Anthropic SDK we're dealing with
37
- # v1.x has a "resources" attribute, v0.x doesn't
38
- is_v1 = hasattr(anthropic, "resources")
39
-
40
- if is_v1:
41
- # Handle newer v1.x SDK
42
- logger.info("Detected Anthropic SDK v1.x")
43
- _ai_anthropic_v1(anthropic)
44
- else:
45
- # Handle older v0.x SDK
46
- logger.info("Detected Anthropic SDK v0.x")
47
- _ai_anthropic_v0(anthropic)
48
-
49
- logger.info("Anthropic client has been wrapped by osmosis-ai.")
50
-
51
-
52
- def _ai_anthropic_v1(anthropic_module):
53
- """Handle wrapping for newer v1.x SDK with resources structure"""
54
- try:
55
- # Get the resources.messages module and class
56
- messages_module = anthropic_module.resources.messages
57
- messages_class = messages_module.Messages
58
-
59
- logger.info(f"Found Anthropic messages class: {messages_class}")
60
-
61
- # Patch the Messages.create method
62
- original_messages_create = messages_class.create
63
- logger.info(f"Original create method: {original_messages_create}")
64
-
65
- if not hasattr(original_messages_create, "_osmosis_aiped"):
66
-
67
- @functools.wraps(original_messages_create)
68
- def wrapped_messages_create(self, *args, **kwargs):
69
- logger.debug(
70
- f"Wrapped create called with args: {args}, kwargs: {kwargs}"
71
- )
72
- try:
73
- # Check if the call includes tool use parameters
74
- has_tools = "tools" in kwargs
75
- if has_tools:
76
- logger.debug(
77
- f"Tool use detected with {len(kwargs['tools'])} tools"
78
- )
79
-
80
- # Check if this is a tool response message
81
- if "messages" in kwargs:
82
- for message in kwargs["messages"]:
83
- if message.get("role") == "user" and isinstance(
84
- message.get("content"), list
85
- ):
86
- for content_item in message["content"]:
87
- if (
88
- isinstance(content_item, dict)
89
- and content_item.get("type") == "tool_response"
90
- ):
91
- logger.debug(
92
- "Tool response detected in messages"
93
- )
94
- break
95
-
96
- response = original_messages_create(self, *args, **kwargs)
97
-
98
- if utils.enabled:
99
- logger.debug("Sending success to OSMOSIS (success)")
100
- send_to_osmosis(
101
- query=kwargs,
102
- response=(
103
- response.model_dump()
104
- if hasattr(response, "model_dump")
105
- else response
106
- ),
107
- status=200,
108
- )
109
-
110
- return response
111
- except Exception as e:
112
- logger.error(f"Error in wrapped create: {e}")
113
- if utils.enabled:
114
- error_response = {"error": str(e)}
115
- send_to_osmosis(
116
- query=kwargs, response=error_response, status=400
117
- )
118
- logger.debug("Sending error to OSMOSIS (success)")
119
- raise # Re-raise the exception
120
-
121
- wrapped_messages_create._osmosis_aiped = True
122
- messages_class.create = wrapped_messages_create
123
- logger.info("Successfully wrapped Messages.create method")
124
-
125
- # Directly wrap the AsyncAnthropic client
126
- try:
127
- # Get the AsyncAnthropic class
128
- AsyncAnthropicClass = anthropic_module.AsyncAnthropic
129
- logger.info(f"Found AsyncAnthropic class: {AsyncAnthropicClass}")
130
-
131
- # Store the original __init__ to keep track of created instances
132
- original_async_init = AsyncAnthropicClass.__init__
133
-
134
- if not hasattr(original_async_init, "_osmosis_aiped"):
135
-
136
- @functools.wraps(original_async_init)
137
- def wrapped_async_init(self, *args, **kwargs):
138
- # Call the original init
139
- result = original_async_init(self, *args, **kwargs)
140
-
141
- logger.info(
142
- "Wrapping new AsyncAnthropic instance's messages.create method"
143
- )
144
-
145
- # Get the messages client from this instance
146
- async_messages = self.messages
147
-
148
- # Store and patch the create method if not already wrapped
149
- if hasattr(async_messages, "create") and not hasattr(
150
- async_messages.create, "_osmosis_aiped"
151
- ):
152
- original_async_messages_create = async_messages.create
153
-
154
- @functools.wraps(original_async_messages_create)
155
- async def wrapped_async_messages_create(*args, **kwargs):
156
- logger.debug(
157
- f"AsyncAnthropic.messages.create called with args: {args}, kwargs: {kwargs}"
158
- )
159
- try:
160
- response = await original_async_messages_create(
161
- *args, **kwargs
162
- )
163
-
164
- if utils.enabled:
165
- logger.debug(
166
- "Sending AsyncAnthropic response to OSMOSIS (success)"
167
- )
168
- send_to_osmosis(
169
- query=kwargs,
170
- response=(
171
- response.model_dump()
172
- if hasattr(response, "model_dump")
173
- else response
174
- ),
175
- status=200,
176
- )
177
-
178
- return response
179
- except Exception as e:
180
- logger.error(
181
- f"Error in wrapped AsyncAnthropic.messages.create: {e}"
182
- )
183
- if utils.enabled:
184
- logger.debug(
185
- "Sending AsyncAnthropic error to OSMOSIS"
186
- )
187
- error_response = {"error": str(e)}
188
- send_to_osmosis(
189
- query=kwargs,
190
- response=error_response,
191
- status=400,
192
- )
193
- raise # Re-raise the exception
194
-
195
- wrapped_async_messages_create._osmosis_aiped = True
196
- async_messages.create = wrapped_async_messages_create
197
- logger.info(
198
- "Successfully wrapped AsyncAnthropic.messages.create method"
199
- )
200
-
201
- return result
202
-
203
- wrapped_async_init._osmosis_aiped = True
204
- AsyncAnthropicClass.__init__ = wrapped_async_init
205
- logger.info(
206
- "Successfully wrapped AsyncAnthropic.__init__ to patch message methods on new instances"
207
- )
208
- except (ImportError, AttributeError) as e:
209
- logger.warning(
210
- f"AsyncAnthropic class not found or has unexpected structure: {e}"
211
- )
212
-
213
- # For compatibility, still try to patch the old-style acreate method if it exists
214
- if hasattr(messages_class, "acreate"):
215
- original_acreate = messages_class.acreate
216
- if not hasattr(original_acreate, "_osmosis_aiped"):
217
-
218
- @functools.wraps(original_acreate)
219
- async def wrapped_acreate(self, *args, **kwargs):
220
- logger.debug(
221
- f"Wrapped async create called with args: {args}, kwargs: {kwargs}"
222
- )
223
- try:
224
- # Check if the async call includes tool use parameters
225
- has_tools = "tools" in kwargs
226
- if has_tools:
227
- logger.debug(
228
- f"Async tool use detected with {len(kwargs['tools'])} tools"
229
- )
230
-
231
- if "messages" in kwargs:
232
- for message in kwargs["messages"]:
233
- if message.get("role") == "user" and isinstance(
234
- message.get("content"), list
235
- ):
236
- for content_item in message["content"]:
237
- if (
238
- isinstance(content_item, dict)
239
- and content_item.get("type")
240
- == "tool_response"
241
- ):
242
- logger.debug(
243
- "Async tool response detected in messages"
244
- )
245
- break
246
-
247
- response = await original_acreate(self, *args, **kwargs)
248
-
249
- if utils.enabled:
250
- logger.debug("Sending async response to OSMOSIS (success)")
251
- send_to_osmosis(
252
- query=kwargs,
253
- response=(
254
- response.model_dump()
255
- if hasattr(response, "model_dump")
256
- else response
257
- ),
258
- status=200,
259
- )
260
-
261
- return response
262
- except Exception as e:
263
- logger.error(f"Error in wrapped async create: {e}")
264
- if utils.enabled:
265
- logger.debug("Sending async error to OSMOSIS")
266
- error_response = {"error": str(e)}
267
- send_to_osmosis(
268
- query=kwargs, response=error_response, status=400
269
- )
270
- raise # Re-raise the exception
271
-
272
- wrapped_acreate._osmosis_aiped = True
273
- messages_class.acreate = wrapped_acreate
274
- logger.info("Successfully wrapped Messages.acreate method")
275
- else:
276
- logger.info("No async acreate method found in Messages class")
277
-
278
- # Check if Completions exists and wrap it if it does
279
- try:
280
- completions_module = anthropic_module.resources.completions
281
- completions_class = completions_module.Completions
282
-
283
- original_completions_create = completions_class.create
284
- if not hasattr(original_completions_create, "_osmosis_aiped"):
285
-
286
- @functools.wraps(original_completions_create)
287
- def wrapped_completions_create(self, *args, **kwargs):
288
- response = original_completions_create(self, *args, **kwargs)
289
-
290
- if utils.enabled:
291
- send_to_osmosis(
292
- query=kwargs,
293
- response=(
294
- response.model_dump()
295
- if hasattr(response, "model_dump")
296
- else response
297
- ),
298
- status=200,
299
- )
300
-
301
- return response
302
-
303
- wrapped_completions_create._osmosis_aiped = True
304
- completions_class.create = wrapped_completions_create
305
-
306
- # Patch the async create method if it exists
307
- if hasattr(completions_class, "acreate"):
308
- original_completions_acreate = completions_class.acreate
309
- if not hasattr(original_completions_acreate, "_osmosis_aiped"):
310
-
311
- @functools.wraps(original_completions_acreate)
312
- async def wrapped_completions_acreate(self, *args, **kwargs):
313
- logger.debug(
314
- f"Wrapped Completions async create called with args: {args}, kwargs: {kwargs}"
315
- )
316
- try:
317
- response = await original_completions_acreate(
318
- self, *args, **kwargs
319
- )
320
-
321
- if utils.enabled:
322
- logger.debug(
323
- "Sending Completions async response to OSMOSIS (success)"
324
- )
325
- send_to_osmosis(
326
- query=kwargs,
327
- response=(
328
- response.model_dump()
329
- if hasattr(response, "model_dump")
330
- else response
331
- ),
332
- status=200,
333
- )
334
-
335
- return response
336
- except Exception as e:
337
- logger.error(
338
- f"Error in wrapped Completions async create: {e}"
339
- )
340
- if utils.enabled:
341
- logger.debug(
342
- "Sending Completions async error to OSMOSIS"
343
- )
344
- error_response = {"error": str(e)}
345
- send_to_osmosis(
346
- query=kwargs,
347
- response=error_response,
348
- status=400,
349
- )
350
- raise # Re-raise the exception
351
-
352
- wrapped_completions_acreate._osmosis_aiped = True
353
- completions_class.acreate = wrapped_completions_acreate
354
- logger.info("Successfully wrapped Completions.acreate method")
355
- else:
356
- logger.info("Completions.acreate already wrapped")
357
- else:
358
- logger.info("No async acreate method found in Completions class")
359
- else:
360
- logger.info("Completions.create already wrapped")
361
- except (ImportError, AttributeError) as e:
362
- # Completions module may not exist in this version
363
- logger.warning(
364
- f"Completions module not found or has an unexpected structure: {e}"
365
- )
366
- except Exception as e:
367
- logger.error(f"Error wrapping Anthropic v1.x client: {e}")
368
-
369
-
370
- def _ai_anthropic_v0(anthropic_module):
371
- """Handle wrapping for older v0.x SDK without resources structure"""
372
- try:
373
- # Get the main Anthropic class
374
- AnthropicClass = anthropic_module.Anthropic
375
- logger.info(f"Found Anthropic class: {AnthropicClass}")
376
-
377
- # Patch the create_completion method for v0.x
378
- if hasattr(AnthropicClass, "complete"):
379
- original_complete = AnthropicClass.complete
380
-
381
- if not hasattr(original_complete, "_osmosis_aiped"):
382
-
383
- @functools.wraps(original_complete)
384
- def wrapped_complete(self, *args, **kwargs):
385
- logger.debug(
386
- f"Wrapped complete called with args: {args}, kwargs: {kwargs}"
387
- )
388
- try:
389
- response = original_complete(self, *args, **kwargs)
390
-
391
- if utils.enabled:
392
- logger.debug("Sending success to OSMOSIS (success)")
393
- send_to_osmosis(query=kwargs, response=response, status=200)
394
-
395
- return response
396
- except Exception as e:
397
- logger.error(f"Error in wrapped complete: {e}")
398
- if utils.enabled:
399
- error_response = {"error": str(e)}
400
- send_to_osmosis(
401
- query=kwargs, response=error_response, status=400
402
- )
403
- logger.debug("Sending error to OSMOSIS (success)")
404
- raise # Re-raise the exception
405
-
406
- wrapped_complete._osmosis_aiped = True
407
- AnthropicClass.complete = wrapped_complete
408
- logger.info("Successfully wrapped Anthropic.complete method")
409
-
410
- # Patch the create_message method for v0.x if it exists
411
- if hasattr(AnthropicClass, "messages"):
412
- logger.info("Found messages client on Anthropic class")
413
-
414
- if hasattr(AnthropicClass.messages, "create"):
415
- original_messages_create = AnthropicClass.messages.create
416
-
417
- if not hasattr(original_messages_create, "_osmosis_aiped"):
418
-
419
- @functools.wraps(original_messages_create)
420
- def wrapped_messages_create(self, *args, **kwargs):
421
- logger.debug(
422
- f"Wrapped messages.create called with args: {args}, kwargs: {kwargs}"
423
- )
424
- try:
425
- response = original_messages_create(self, *args, **kwargs)
426
-
427
- if utils.enabled:
428
- logger.debug("Sending success to OSMOSIS (success)")
429
- send_to_osmosis(
430
- query=kwargs, response=response, status=200
431
- )
432
-
433
- return response
434
- except Exception as e:
435
- logger.error(f"Error in wrapped messages.create: {e}")
436
- if utils.enabled:
437
- error_response = {"error": str(e)}
438
- send_to_osmosis(
439
- query=kwargs, response=error_response, status=400
440
- )
441
- logger.debug("Sending error to OSMOSIS (success)")
442
- raise # Re-raise the exception
443
-
444
- wrapped_messages_create._osmosis_aiped = True
445
- AnthropicClass.messages.create = wrapped_messages_create
446
- logger.info("Successfully wrapped Anthropic.messages.create method")
447
-
448
- # Also try to patch instance methods by monkeypatching the __init__
449
- original_init = AnthropicClass.__init__
450
-
451
- if not hasattr(original_init, "_osmosis_aiped"):
452
-
453
- @functools.wraps(original_init)
454
- def wrapped_init(self, *args, **kwargs):
455
- # Call the original init
456
- result = original_init(self, *args, **kwargs)
457
-
458
- # Wrap the instance methods if they exist
459
- if hasattr(self, "complete") and not hasattr(
460
- self.complete, "_osmosis_aiped"
461
- ):
462
- original_instance_complete = self.complete
463
-
464
- @functools.wraps(original_instance_complete)
465
- def wrapped_instance_complete(*args, **kwargs):
466
- logger.debug(
467
- f"Instance complete called with args: {args}, kwargs: {kwargs}"
468
- )
469
- try:
470
- response = original_instance_complete(*args, **kwargs)
471
-
472
- if utils.enabled:
473
- logger.debug("Sending success to OSMOSIS (success)")
474
- send_to_osmosis(
475
- query=kwargs, response=response, status=200
476
- )
477
-
478
- return response
479
- except Exception as e:
480
- logger.error(f"Error in wrapped instance complete: {e}")
481
- if utils.enabled:
482
- error_response = {"error": str(e)}
483
- send_to_osmosis(
484
- query=kwargs,
485
- response=error_response,
486
- status=400,
487
- )
488
- raise
489
-
490
- wrapped_instance_complete._osmosis_aiped = True
491
- self.complete = wrapped_instance_complete
492
-
493
- return result
494
-
495
- wrapped_init._osmosis_aiped = True
496
- AnthropicClass.__init__ = wrapped_init
497
- logger.info(
498
- "Successfully wrapped Anthropic.__init__ to patch instance methods"
499
- )
500
-
501
- except Exception as e:
502
- logger.error(f"Error wrapping Anthropic v0.x client: {e}")