|
27 | 27 | "import typing as t\n", |
28 | 28 | "import asyncio\n", |
29 | 29 | "import inspect\n", |
| 30 | + "import threading\n", |
30 | 31 | "from pydantic import BaseModel\n", |
31 | 32 | "import instructor\n", |
32 | 33 | "\n", |
33 | 34 | "T = t.TypeVar('T', bound=BaseModel)\n", |
34 | 35 | "\n", |
35 | 36 | "class RagasLLM:\n", |
36 | | - " def __init__(self, provider: str, model:str, client: t.Any, **model_args):\n", |
| 37 | + " def __init__(self, provider: str, model: str, client: t.Any, **model_args):\n", |
37 | 38 | " self.provider = provider.lower()\n", |
38 | 39 | " self.model = model\n", |
39 | 40 | " self.model_args = model_args or {}\n", |
|
70 | 71 | " def _run_async_in_current_loop(self, coro):\n", |
71 | 72 | " \"\"\"Run an async coroutine in the current event loop if possible.\n", |
72 | 73 | " \n", |
73 | | - " This handles Jupyter environments correctly by using the existing loop.\n", |
| 74 | + " This handles Jupyter environments correctly by using a separate thread\n", |
| 75 | + " when a running event loop is detected.\n", |
74 | 76 | " \"\"\"\n", |
75 | 77 | " try:\n", |
76 | | - " # Check if we're in an environment with an existing event loop (like Jupyter)\n", |
| 78 | + " # Try to get the current event loop\n", |
77 | 79 | " loop = asyncio.get_event_loop()\n", |
| 80 | + " \n", |
78 | 81 | " if loop.is_running():\n", |
79 | | - " # We're likely in a Jupyter environment\n", |
80 | | - " import nest_asyncio\n", |
81 | | - " nest_asyncio.apply()\n", |
82 | | - " return loop.run_until_complete(coro)\n", |
| 82 | + " # If the loop is already running (like in Jupyter notebooks),\n", |
| 83 | + " # we run the coroutine in a separate thread with its own event loop\n", |
| 84 | + " result_container = {'result': None, 'exception': None}\n", |
| 85 | + " \n", |
| 86 | + " def run_in_thread():\n", |
| 87 | + " # Create a new event loop for this thread\n", |
| 88 | + " new_loop = asyncio.new_event_loop()\n", |
| 89 | + " asyncio.set_event_loop(new_loop)\n", |
| 90 | + " try:\n", |
| 91 | + " # Run the coroutine in this thread's event loop\n", |
| 92 | + " result_container['result'] = new_loop.run_until_complete(coro)\n", |
| 93 | + " except Exception as e:\n", |
| 94 | + " # Capture any exceptions to re-raise in the main thread\n", |
| 95 | + " result_container['exception'] = e\n", |
| 96 | + " finally:\n", |
| 97 | + " # Clean up the event loop\n", |
| 98 | + " new_loop.close()\n", |
| 99 | + " \n", |
| 100 | + " # Start the thread and wait for it to complete\n", |
| 101 | + " thread = threading.Thread(target=run_in_thread)\n", |
| 102 | + " thread.start()\n", |
| 103 | + " thread.join()\n", |
| 104 | + " \n", |
| 105 | + " # Re-raise any exceptions that occurred in the thread\n", |
| 106 | + " if result_container['exception']:\n", |
| 107 | + " raise result_container['exception']\n", |
| 108 | + " \n", |
| 109 | + " return result_container['result']\n", |
| 110 | + " else:\n", |
| 111 | + " # Standard case - event loop exists but isn't running\n", |
| 112 | + " return loop.run_until_complete(coro)\n", |
| 113 | + " \n", |
83 | 114 | " except RuntimeError:\n", |
84 | 115 | " # If we get a runtime error about no event loop, create a new one\n", |
85 | 116 | " loop = asyncio.new_event_loop()\n", |
86 | 117 | " asyncio.set_event_loop(loop)\n", |
87 | 118 | " try:\n", |
88 | 119 | " return loop.run_until_complete(coro)\n", |
89 | 120 | " finally:\n", |
| 121 | + " # Clean up\n", |
90 | 122 | " loop.close()\n", |
91 | 123 | " asyncio.set_event_loop(None)\n", |
92 | 124 | " \n", |
|
129 | 161 | " **self.model_args,\n", |
130 | 162 | " )\n", |
131 | 163 | "\n", |
132 | | - "def ragas_llm(provider: str,model:str, client: t.Any, **model_args) -> RagasLLM:\n", |
| 164 | + "def ragas_llm(provider: str, model: str, client: t.Any, **model_args) -> RagasLLM:\n", |
133 | 165 | " return RagasLLM(provider=provider, client=client, model=model, **model_args)" |
134 | 166 | ] |
135 | 167 | }, |
|
0 commit comments