reminix-llamaindex 0.0.1__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.
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""LlamaIndex adapter for Reminix Runtime."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from collections.abc import AsyncIterator
|
|
5
|
+
from typing import Any, Protocol, runtime_checkable
|
|
6
|
+
|
|
7
|
+
from reminix_runtime import (
|
|
8
|
+
BaseAdapter,
|
|
9
|
+
ChatRequest,
|
|
10
|
+
ChatResponse,
|
|
11
|
+
InvokeRequest,
|
|
12
|
+
InvokeResponse,
|
|
13
|
+
Message,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@runtime_checkable
|
|
18
|
+
class ChatEngine(Protocol):
|
|
19
|
+
"""Protocol for LlamaIndex chat engines."""
|
|
20
|
+
|
|
21
|
+
async def achat(self, message: str) -> Any:
|
|
22
|
+
"""Async chat method."""
|
|
23
|
+
...
|
|
24
|
+
|
|
25
|
+
async def astream_chat(self, message: str) -> Any:
|
|
26
|
+
"""Async streaming chat method."""
|
|
27
|
+
...
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class LlamaIndexAdapter(BaseAdapter):
|
|
31
|
+
"""Adapter for LlamaIndex chat engines."""
|
|
32
|
+
|
|
33
|
+
adapter_name = "llamaindex"
|
|
34
|
+
|
|
35
|
+
def __init__(self, engine: ChatEngine, name: str = "llamaindex-agent") -> None:
|
|
36
|
+
"""Initialize the adapter.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
engine: A LlamaIndex chat engine (e.g., SimpleChatEngine, ContextChatEngine).
|
|
40
|
+
name: Name for the agent.
|
|
41
|
+
"""
|
|
42
|
+
self._engine = engine
|
|
43
|
+
self._name = name
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def name(self) -> str:
|
|
47
|
+
return self._name
|
|
48
|
+
|
|
49
|
+
def _get_last_user_message(self, messages: list[Message]) -> str:
|
|
50
|
+
"""Get the last user message from the conversation."""
|
|
51
|
+
for message in reversed(messages):
|
|
52
|
+
if message.role == "user":
|
|
53
|
+
return message.content or ""
|
|
54
|
+
# Fallback to last message if no user message found
|
|
55
|
+
return messages[-1].content or "" if messages else ""
|
|
56
|
+
|
|
57
|
+
async def invoke(self, request: InvokeRequest) -> InvokeResponse:
|
|
58
|
+
"""Handle an invoke request.
|
|
59
|
+
|
|
60
|
+
For task-oriented operations. Expects input with 'query' or 'prompt' key.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
request: The invoke request with input data.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
The invoke response with the output.
|
|
67
|
+
"""
|
|
68
|
+
# Extract query from input
|
|
69
|
+
if "query" in request.input:
|
|
70
|
+
query = request.input["query"]
|
|
71
|
+
elif "prompt" in request.input:
|
|
72
|
+
query = request.input["prompt"]
|
|
73
|
+
elif "message" in request.input:
|
|
74
|
+
query = request.input["message"]
|
|
75
|
+
else:
|
|
76
|
+
query = str(request.input)
|
|
77
|
+
|
|
78
|
+
# Call the chat engine
|
|
79
|
+
response = await self._engine.achat(query)
|
|
80
|
+
|
|
81
|
+
# Extract content from response
|
|
82
|
+
output = str(response.response) if hasattr(response, "response") else str(response)
|
|
83
|
+
|
|
84
|
+
return InvokeResponse(output=output)
|
|
85
|
+
|
|
86
|
+
async def chat(self, request: ChatRequest) -> ChatResponse:
|
|
87
|
+
"""Handle a chat request.
|
|
88
|
+
|
|
89
|
+
For conversational interactions. Sends the last user message to the engine.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
request: The chat request with messages.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
The chat response with output and messages.
|
|
96
|
+
"""
|
|
97
|
+
# Get the last user message to send to the engine
|
|
98
|
+
message = self._get_last_user_message(request.messages)
|
|
99
|
+
|
|
100
|
+
# Call the chat engine
|
|
101
|
+
response = await self._engine.achat(message)
|
|
102
|
+
|
|
103
|
+
# Extract content from response
|
|
104
|
+
output = str(response.response) if hasattr(response, "response") else str(response)
|
|
105
|
+
|
|
106
|
+
# Build response messages (original + assistant response)
|
|
107
|
+
response_messages: list[dict[str, Any]] = [
|
|
108
|
+
{"role": m.role, "content": m.content} for m in request.messages
|
|
109
|
+
]
|
|
110
|
+
response_messages.append({"role": "assistant", "content": output})
|
|
111
|
+
|
|
112
|
+
return ChatResponse(output=output, messages=response_messages)
|
|
113
|
+
|
|
114
|
+
async def invoke_stream(self, request: InvokeRequest) -> AsyncIterator[str]:
|
|
115
|
+
"""Handle a streaming invoke request.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
request: The invoke request with input data.
|
|
119
|
+
|
|
120
|
+
Yields:
|
|
121
|
+
JSON-encoded chunks from the stream.
|
|
122
|
+
"""
|
|
123
|
+
# Extract query from input
|
|
124
|
+
if "query" in request.input:
|
|
125
|
+
query = request.input["query"]
|
|
126
|
+
elif "prompt" in request.input:
|
|
127
|
+
query = request.input["prompt"]
|
|
128
|
+
elif "message" in request.input:
|
|
129
|
+
query = request.input["message"]
|
|
130
|
+
else:
|
|
131
|
+
query = str(request.input)
|
|
132
|
+
|
|
133
|
+
# Stream from the chat engine
|
|
134
|
+
response = await self._engine.astream_chat(query)
|
|
135
|
+
async for token in response.async_response_gen():
|
|
136
|
+
yield json.dumps({"chunk": token})
|
|
137
|
+
|
|
138
|
+
async def chat_stream(self, request: ChatRequest) -> AsyncIterator[str]:
|
|
139
|
+
"""Handle a streaming chat request.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
request: The chat request with messages.
|
|
143
|
+
|
|
144
|
+
Yields:
|
|
145
|
+
JSON-encoded chunks from the stream.
|
|
146
|
+
"""
|
|
147
|
+
# Get the last user message to send to the engine
|
|
148
|
+
message = self._get_last_user_message(request.messages)
|
|
149
|
+
|
|
150
|
+
# Stream from the chat engine
|
|
151
|
+
response = await self._engine.astream_chat(message)
|
|
152
|
+
async for token in response.async_response_gen():
|
|
153
|
+
yield json.dumps({"chunk": token})
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def wrap(engine: ChatEngine, name: str = "llamaindex-agent") -> LlamaIndexAdapter:
|
|
157
|
+
"""Wrap a LlamaIndex chat engine for use with Reminix Runtime.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
engine: A LlamaIndex chat engine (e.g., SimpleChatEngine, ContextChatEngine).
|
|
161
|
+
name: Name for the agent.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
A LlamaIndexAdapter instance.
|
|
165
|
+
|
|
166
|
+
Example:
|
|
167
|
+
```python
|
|
168
|
+
from llama_index.core.chat_engine import SimpleChatEngine
|
|
169
|
+
from llama_index.llms.openai import OpenAI
|
|
170
|
+
from reminix_llamaindex import wrap
|
|
171
|
+
from reminix_runtime import serve
|
|
172
|
+
|
|
173
|
+
llm = OpenAI(model="gpt-4")
|
|
174
|
+
engine = SimpleChatEngine.from_defaults(llm=llm)
|
|
175
|
+
agent = wrap(engine, name="my-agent")
|
|
176
|
+
serve([agent], port=8080)
|
|
177
|
+
```
|
|
178
|
+
"""
|
|
179
|
+
return LlamaIndexAdapter(engine, name=name)
|
|
File without changes
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: reminix-llamaindex
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Reminix adapter for LlamaIndex
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Requires-Dist: llama-index>=0.14.0
|
|
8
|
+
Requires-Dist: reminix-runtime~=0.0.1
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
|
|
11
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# reminix-llamaindex
|
|
15
|
+
|
|
16
|
+
Reminix Runtime adapter for [LlamaIndex](https://www.llamaindex.ai/). Deploy LlamaIndex chat engines as a REST API.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install reminix-llamaindex
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
This will also install `reminix-runtime` as a dependency.
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from llama_index.core.chat_engine import SimpleChatEngine
|
|
30
|
+
from llama_index.llms.openai import OpenAI
|
|
31
|
+
from reminix_llamaindex import wrap
|
|
32
|
+
from reminix_runtime import serve
|
|
33
|
+
|
|
34
|
+
# Create a LlamaIndex chat engine
|
|
35
|
+
llm = OpenAI(model="gpt-4o")
|
|
36
|
+
engine = SimpleChatEngine.from_defaults(llm=llm)
|
|
37
|
+
|
|
38
|
+
# Wrap it with the Reminix adapter
|
|
39
|
+
agent = wrap(engine, name="my-chatbot")
|
|
40
|
+
|
|
41
|
+
# Serve it as a REST API
|
|
42
|
+
serve([agent], port=8080)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Your agent is now available at:
|
|
46
|
+
- `POST /agents/my-chatbot/invoke` - Stateless invocation
|
|
47
|
+
- `POST /agents/my-chatbot/chat` - Conversational chat
|
|
48
|
+
|
|
49
|
+
## API Reference
|
|
50
|
+
|
|
51
|
+
### `wrap(engine, name)`
|
|
52
|
+
|
|
53
|
+
Wrap a LlamaIndex chat engine for use with Reminix Runtime.
|
|
54
|
+
|
|
55
|
+
| Parameter | Type | Default | Description |
|
|
56
|
+
|-----------|------|---------|-------------|
|
|
57
|
+
| `engine` | `BaseChatEngine` | required | A LlamaIndex chat engine |
|
|
58
|
+
| `name` | `str` | `"llamaindex-agent"` | Name for the agent (used in URL path) |
|
|
59
|
+
|
|
60
|
+
**Returns:** `LlamaIndexAdapter` - A Reminix adapter instance
|
|
61
|
+
|
|
62
|
+
### Example with RAG
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
|
|
66
|
+
from llama_index.llms.openai import OpenAI
|
|
67
|
+
from reminix_llamaindex import wrap
|
|
68
|
+
from reminix_runtime import serve
|
|
69
|
+
|
|
70
|
+
# Load documents and create index
|
|
71
|
+
documents = SimpleDirectoryReader("./data").load_data()
|
|
72
|
+
index = VectorStoreIndex.from_documents(documents)
|
|
73
|
+
|
|
74
|
+
# Create a chat engine with the index
|
|
75
|
+
engine = index.as_chat_engine(llm=OpenAI(model="gpt-4o"))
|
|
76
|
+
|
|
77
|
+
# Wrap and serve
|
|
78
|
+
agent = wrap(engine, name="rag-chatbot")
|
|
79
|
+
serve([agent], port=8080)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Endpoint Input/Output Formats
|
|
83
|
+
|
|
84
|
+
### POST /agents/{name}/invoke
|
|
85
|
+
|
|
86
|
+
Stateless invocation for task-oriented operations.
|
|
87
|
+
|
|
88
|
+
**Request:**
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"input": {
|
|
92
|
+
"query": "What is the capital of France?"
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Or with prompt:
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"input": {
|
|
101
|
+
"prompt": "Summarize this text: ..."
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Response:**
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"output": "The capital of France is Paris."
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### POST /agents/{name}/chat
|
|
114
|
+
|
|
115
|
+
Conversational chat. The adapter extracts the last user message.
|
|
116
|
+
|
|
117
|
+
**Request:**
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"messages": [
|
|
121
|
+
{"role": "user", "content": "What is the capital of France?"}
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Response:**
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"output": "The capital of France is Paris.",
|
|
130
|
+
"messages": [
|
|
131
|
+
{"role": "user", "content": "What is the capital of France?"},
|
|
132
|
+
{"role": "assistant", "content": "The capital of France is Paris."}
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Runtime Documentation
|
|
138
|
+
|
|
139
|
+
For information about the server, endpoints, request/response formats, and more, see the [`reminix-runtime`](https://pypi.org/project/reminix-runtime/) package.
|
|
140
|
+
|
|
141
|
+
## Links
|
|
142
|
+
|
|
143
|
+
- [GitHub Repository](https://github.com/reminix-ai/runtime-python)
|
|
144
|
+
- [LlamaIndex Documentation](https://docs.llamaindex.ai/)
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
Apache-2.0
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
reminix_llamaindex/__init__.py,sha256=F6k9DeJDD5-cwD0VlVNnu4aaIssuWQ2Uyp5eEGkEc84,86
|
|
2
|
+
reminix_llamaindex/adapter.py,sha256=7dVMR6nw8EL9A7KeavAwAvhFsKRO6pLdUIQRD6IH4UM,5691
|
|
3
|
+
reminix_llamaindex/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
reminix_llamaindex-0.0.1.dist-info/METADATA,sha256=lo4BbUew1TOMCtbBvtCepDEu56tmJu22sCOd2g3tGRM,3368
|
|
5
|
+
reminix_llamaindex-0.0.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
6
|
+
reminix_llamaindex-0.0.1.dist-info/RECORD,,
|