portazgo 0.1.0__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.
portazgo-0.1.0/LICENSE ADDED
@@ -0,0 +1,17 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ Copyright 2025 IBM, Red Hat
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
@@ -0,0 +1,4 @@
1
+ include LICENSE README.md pyproject.toml
2
+ recursive-include src *
3
+ recursive-exclude * __pycache__
4
+ recursive-exclude * *.py[co]
@@ -0,0 +1,247 @@
1
+ Metadata-Version: 2.4
2
+ Name: portazgo
3
+ Version: 0.1.0
4
+ Summary: Pluggable agent SDK to talk to Llama Stack using a cohort of agents (default Llama Stack Responses API or LangGraph)
5
+ Author: Alpha Hack Program
6
+ License: Apache-2.0
7
+ Project-URL: Repository, https://github.com/alpha-hack-program/llama-stack-demo
8
+ Project-URL: Documentation, https://github.com/alpha-hack-program/llama-stack-demo#readme
9
+ Keywords: rag,ragas,llama-stack,langgraph,agent
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: Apache Software License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Python: >=3.12
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: httpx
19
+ Requires-Dist: llama-stack-client<0.4,>=0.3.5
20
+ Provides-Extra: dev
21
+ Requires-Dist: pytest>=7.0; extra == "dev"
22
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
23
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
24
+ Requires-Dist: build>=1.0; extra == "dev"
25
+ Requires-Dist: python-dotenv>=1.0; extra == "dev"
26
+ Provides-Extra: langgraph
27
+ Requires-Dist: langgraph>=0.2; extra == "langgraph"
28
+ Dynamic: license-file
29
+
30
+ # cohorte
31
+
32
+ Pluggable agent backends for RAGAS dataset generation. Use the **default** backend (Llama Stack Responses API) or a **lang-graph** backend (stub for future implementation).
33
+
34
+ ## Installation
35
+
36
+ From source with **uv** (recommended):
37
+
38
+ ```bash
39
+ cd cohorte
40
+ uv sync --extra dev
41
+ ```
42
+
43
+ With optional LangGraph extra (when that backend is implemented):
44
+
45
+ ```bash
46
+ uv sync --extra dev --extra langgraph
47
+ ```
48
+
49
+ With pip (from source):
50
+
51
+ ```bash
52
+ pip install -e .
53
+ ```
54
+
55
+ For PyPI (once published):
56
+
57
+ ```bash
58
+ pip install cohorte
59
+ ```
60
+
61
+ ## Usage
62
+
63
+ ### Agent with type
64
+
65
+ ```python
66
+ from cohorte import Agent
67
+
68
+ # Default: Llama Stack Responses API (same as ragas_pipeline / ragas_dataset_generator)
69
+ agent = Agent(type="default")
70
+ ragas_dataset = agent.generate_ragas_dataset(
71
+ base_dataset=base_dataset,
72
+ client=llama_stack_client,
73
+ model_id="my-model",
74
+ vector_store_id=vs_id,
75
+ mcp_tools=mcp_tools,
76
+ instructions="Optional system prompt",
77
+ )
78
+ ```
79
+
80
+ ### Single query: invoke (normal agent call)
81
+
82
+ Same parameter shape as `generate_ragas_dataset`, but for one input. The name follows **LangChain/LangGraph** (`agent.invoke(input)`):
83
+
84
+ ```python
85
+ from cohorte import Agent
86
+
87
+ agent = Agent(type="default")
88
+ result = agent.invoke(
89
+ "What is the capital of France?",
90
+ client=llama_stack_client,
91
+ model_id="my-model",
92
+ vector_store_id=vs_id,
93
+ mcp_tools=[], # or list of MCP tool configs
94
+ instructions="You are a helpful assistant.",
95
+ )
96
+ # result["answer"] -> str
97
+ # result["contexts"] -> list[str] (retrieved chunks + non–file_search tool responses)
98
+ # result["tool_calls"] -> list[dict]
99
+ ```
100
+
101
+ ### Chat with history (e.g. chatbots)
102
+
103
+ Pass `messages` so the model sees previous turns. Each message is `{"role": "user"|"assistant"|"system", "content": str}`:
104
+
105
+ ```python
106
+ history = [
107
+ {"role": "user", "content": "My name is Alice."},
108
+ {"role": "assistant", "content": "Nice to meet you, Alice!"},
109
+ ]
110
+ result = agent.invoke(
111
+ "What's my name?",
112
+ client=client,
113
+ model_id=model_id,
114
+ vector_store_id=vs_id,
115
+ mcp_tools=[],
116
+ messages=history,
117
+ )
118
+ # result["answer"] can refer to the conversation (e.g. "Your name is Alice.")
119
+ ```
120
+
121
+ ### Streaming: invoke_stream
122
+
123
+ For real-time display (e.g. Streamlit), use `invoke_stream`. It yields events: `content_delta` (chunk of text) then `done` (final answer + contexts + tool_calls). If the backend does not support token-level streaming, the full answer is sent as one delta then `done`.
124
+
125
+ ```python
126
+ for event in agent.invoke_stream(
127
+ "Explain RAG in one sentence.",
128
+ client=client,
129
+ model_id=model_id,
130
+ vector_store_id=vs_id,
131
+ mcp_tools=[],
132
+ messages=st.session_state.messages, # optional history
133
+ ):
134
+ if event["type"] == "content_delta":
135
+ print(event["delta"], end="", flush=True)
136
+ elif event["type"] == "done":
137
+ answer, contexts, tool_calls = event["answer"], event["contexts"], event["tool_calls"]
138
+ ```
139
+
140
+ ### Streamlit chat example (with history + streaming)
141
+
142
+ ```python
143
+ import streamlit as st
144
+ from cohorte import Agent
145
+
146
+ # Init session state
147
+ if "messages" not in st.session_state:
148
+ st.session_state.messages = []
149
+
150
+ agent = Agent(type="default")
151
+ # client, model_id, vector_store_id from your config (e.g. sidebar)
152
+
153
+ # Display history
154
+ for msg in st.session_state.messages:
155
+ with st.chat_message(msg["role"]):
156
+ st.markdown(msg["content"])
157
+
158
+ if prompt := st.chat_input("Your message"):
159
+ st.session_state.messages.append({"role": "user", "content": prompt})
160
+ with st.chat_message("user"):
161
+ st.markdown(prompt)
162
+
163
+ with st.chat_message("assistant"):
164
+ placeholder = st.empty()
165
+ full = ""
166
+ for event in agent.invoke_stream(
167
+ prompt,
168
+ client=client,
169
+ model_id=model_id,
170
+ vector_store_id=vector_store_id,
171
+ mcp_tools=[],
172
+ messages=st.session_state.messages[:-1], # history (exclude current)
173
+ ):
174
+ if event["type"] == "content_delta":
175
+ full += event["delta"]
176
+ placeholder.markdown(full + "▌")
177
+ placeholder.markdown(full)
178
+ st.session_state.messages.append({"role": "assistant", "content": full})
179
+ ```
180
+
181
+ ```python
182
+ # LangGraph backend (not yet implemented; will raise NotImplementedError)
183
+ agent = Agent(type="lang-graph")
184
+ # agent.invoke(...) # NotImplementedError
185
+ ```
186
+
187
+ ### Utilities
188
+
189
+ The library also exposes helpers used by the default backend, useful for custom pipelines:
190
+
191
+ ```python
192
+ from cohorte import strip_think_blocks, serialize_for_json, extract_tool_calls
193
+ ```
194
+
195
+ - `strip_think_blocks(text)` – remove `<think>...</think>` blocks from model output.
196
+ - `serialize_for_json(val)` – convert objects to JSON-serializable form.
197
+ - `extract_tool_calls(response)` – extract tool calls from a Llama Stack response.
198
+
199
+ ## Testing a simple invoke
200
+
201
+ **Option 1: Unit tests (no Llama Stack server)**
202
+ Runs `invoke` against a mock client so you can confirm the API shape:
203
+
204
+ ```bash
205
+ cd cohorte
206
+ uv run pytest tests/test_agent.py -v -k invoke
207
+ ```
208
+
209
+ **Option 2: Real invoke against Llama Stack**
210
+ Use the example script (requires a running Llama Stack and a vector store):
211
+
212
+ ```bash
213
+ cd cohorte
214
+ export LLAMA_STACK_HOST=localhost
215
+ export LLAMA_STACK_PORT=8080
216
+ # optional: AGENT_VECTOR_STORE_NAME=rag-store, AGENT_MODEL_ID="your/model"
217
+
218
+ uv run python examples/simple_invoke.py "What is 2+2?"
219
+ ```
220
+
221
+ You can pass any question as arguments; default is `"What is 2+2?"`.
222
+
223
+ **Option 3: OpenShift (oc)**
224
+ If Llama Stack is exposed on OpenShift, use the helper script to get `APPS_DOMAIN` and run the example:
225
+
226
+ ```bash
227
+ cd cohorte
228
+ ./scripts/run_invoke_oc.sh "What is 2+2?"
229
+ ```
230
+
231
+ The script sources `.env` (for `PROJECT`, etc.), runs `oc get ingresses.config.openshift.io cluster` for the apps domain, sets `LLAMA_STACK_HOST` to `llama-stack-demo-route-${PROJECT}.${APPS_DOMAIN}`, then runs the example with any arguments you pass.
232
+
233
+ ## Development
234
+
235
+ Uses [uv](https://docs.astral.sh/uv/) for the venv and running tools. From the cohorte directory:
236
+
237
+ - **Create venv and install deps:** `make install-dev` (or `uv sync --extra dev`)
238
+ - **Lock dependencies:** `make lock` (or `uv lock`)
239
+ - **Lint:** `make lint` (ruff via `uv run`)
240
+ - **Format:** `make format`
241
+ - **Tests:** `make test` (or `uv run pytest tests`)
242
+ - **Coverage:** `make coverage`
243
+ - **Build:** `make build` (or `uv run python -m build`)
244
+
245
+ ## License
246
+
247
+ Apache-2.0. See [LICENSE](LICENSE).
@@ -0,0 +1,218 @@
1
+ # cohorte
2
+
3
+ Pluggable agent backends for RAGAS dataset generation. Use the **default** backend (Llama Stack Responses API) or a **lang-graph** backend (stub for future implementation).
4
+
5
+ ## Installation
6
+
7
+ From source with **uv** (recommended):
8
+
9
+ ```bash
10
+ cd cohorte
11
+ uv sync --extra dev
12
+ ```
13
+
14
+ With optional LangGraph extra (when that backend is implemented):
15
+
16
+ ```bash
17
+ uv sync --extra dev --extra langgraph
18
+ ```
19
+
20
+ With pip (from source):
21
+
22
+ ```bash
23
+ pip install -e .
24
+ ```
25
+
26
+ For PyPI (once published):
27
+
28
+ ```bash
29
+ pip install cohorte
30
+ ```
31
+
32
+ ## Usage
33
+
34
+ ### Agent with type
35
+
36
+ ```python
37
+ from cohorte import Agent
38
+
39
+ # Default: Llama Stack Responses API (same as ragas_pipeline / ragas_dataset_generator)
40
+ agent = Agent(type="default")
41
+ ragas_dataset = agent.generate_ragas_dataset(
42
+ base_dataset=base_dataset,
43
+ client=llama_stack_client,
44
+ model_id="my-model",
45
+ vector_store_id=vs_id,
46
+ mcp_tools=mcp_tools,
47
+ instructions="Optional system prompt",
48
+ )
49
+ ```
50
+
51
+ ### Single query: invoke (normal agent call)
52
+
53
+ Same parameter shape as `generate_ragas_dataset`, but for one input. The name follows **LangChain/LangGraph** (`agent.invoke(input)`):
54
+
55
+ ```python
56
+ from cohorte import Agent
57
+
58
+ agent = Agent(type="default")
59
+ result = agent.invoke(
60
+ "What is the capital of France?",
61
+ client=llama_stack_client,
62
+ model_id="my-model",
63
+ vector_store_id=vs_id,
64
+ mcp_tools=[], # or list of MCP tool configs
65
+ instructions="You are a helpful assistant.",
66
+ )
67
+ # result["answer"] -> str
68
+ # result["contexts"] -> list[str] (retrieved chunks + non–file_search tool responses)
69
+ # result["tool_calls"] -> list[dict]
70
+ ```
71
+
72
+ ### Chat with history (e.g. chatbots)
73
+
74
+ Pass `messages` so the model sees previous turns. Each message is `{"role": "user"|"assistant"|"system", "content": str}`:
75
+
76
+ ```python
77
+ history = [
78
+ {"role": "user", "content": "My name is Alice."},
79
+ {"role": "assistant", "content": "Nice to meet you, Alice!"},
80
+ ]
81
+ result = agent.invoke(
82
+ "What's my name?",
83
+ client=client,
84
+ model_id=model_id,
85
+ vector_store_id=vs_id,
86
+ mcp_tools=[],
87
+ messages=history,
88
+ )
89
+ # result["answer"] can refer to the conversation (e.g. "Your name is Alice.")
90
+ ```
91
+
92
+ ### Streaming: invoke_stream
93
+
94
+ For real-time display (e.g. Streamlit), use `invoke_stream`. It yields events: `content_delta` (chunk of text) then `done` (final answer + contexts + tool_calls). If the backend does not support token-level streaming, the full answer is sent as one delta then `done`.
95
+
96
+ ```python
97
+ for event in agent.invoke_stream(
98
+ "Explain RAG in one sentence.",
99
+ client=client,
100
+ model_id=model_id,
101
+ vector_store_id=vs_id,
102
+ mcp_tools=[],
103
+ messages=st.session_state.messages, # optional history
104
+ ):
105
+ if event["type"] == "content_delta":
106
+ print(event["delta"], end="", flush=True)
107
+ elif event["type"] == "done":
108
+ answer, contexts, tool_calls = event["answer"], event["contexts"], event["tool_calls"]
109
+ ```
110
+
111
+ ### Streamlit chat example (with history + streaming)
112
+
113
+ ```python
114
+ import streamlit as st
115
+ from cohorte import Agent
116
+
117
+ # Init session state
118
+ if "messages" not in st.session_state:
119
+ st.session_state.messages = []
120
+
121
+ agent = Agent(type="default")
122
+ # client, model_id, vector_store_id from your config (e.g. sidebar)
123
+
124
+ # Display history
125
+ for msg in st.session_state.messages:
126
+ with st.chat_message(msg["role"]):
127
+ st.markdown(msg["content"])
128
+
129
+ if prompt := st.chat_input("Your message"):
130
+ st.session_state.messages.append({"role": "user", "content": prompt})
131
+ with st.chat_message("user"):
132
+ st.markdown(prompt)
133
+
134
+ with st.chat_message("assistant"):
135
+ placeholder = st.empty()
136
+ full = ""
137
+ for event in agent.invoke_stream(
138
+ prompt,
139
+ client=client,
140
+ model_id=model_id,
141
+ vector_store_id=vector_store_id,
142
+ mcp_tools=[],
143
+ messages=st.session_state.messages[:-1], # history (exclude current)
144
+ ):
145
+ if event["type"] == "content_delta":
146
+ full += event["delta"]
147
+ placeholder.markdown(full + "▌")
148
+ placeholder.markdown(full)
149
+ st.session_state.messages.append({"role": "assistant", "content": full})
150
+ ```
151
+
152
+ ```python
153
+ # LangGraph backend (not yet implemented; will raise NotImplementedError)
154
+ agent = Agent(type="lang-graph")
155
+ # agent.invoke(...) # NotImplementedError
156
+ ```
157
+
158
+ ### Utilities
159
+
160
+ The library also exposes helpers used by the default backend, useful for custom pipelines:
161
+
162
+ ```python
163
+ from cohorte import strip_think_blocks, serialize_for_json, extract_tool_calls
164
+ ```
165
+
166
+ - `strip_think_blocks(text)` – remove `<think>...</think>` blocks from model output.
167
+ - `serialize_for_json(val)` – convert objects to JSON-serializable form.
168
+ - `extract_tool_calls(response)` – extract tool calls from a Llama Stack response.
169
+
170
+ ## Testing a simple invoke
171
+
172
+ **Option 1: Unit tests (no Llama Stack server)**
173
+ Runs `invoke` against a mock client so you can confirm the API shape:
174
+
175
+ ```bash
176
+ cd cohorte
177
+ uv run pytest tests/test_agent.py -v -k invoke
178
+ ```
179
+
180
+ **Option 2: Real invoke against Llama Stack**
181
+ Use the example script (requires a running Llama Stack and a vector store):
182
+
183
+ ```bash
184
+ cd cohorte
185
+ export LLAMA_STACK_HOST=localhost
186
+ export LLAMA_STACK_PORT=8080
187
+ # optional: AGENT_VECTOR_STORE_NAME=rag-store, AGENT_MODEL_ID="your/model"
188
+
189
+ uv run python examples/simple_invoke.py "What is 2+2?"
190
+ ```
191
+
192
+ You can pass any question as arguments; default is `"What is 2+2?"`.
193
+
194
+ **Option 3: OpenShift (oc)**
195
+ If Llama Stack is exposed on OpenShift, use the helper script to get `APPS_DOMAIN` and run the example:
196
+
197
+ ```bash
198
+ cd cohorte
199
+ ./scripts/run_invoke_oc.sh "What is 2+2?"
200
+ ```
201
+
202
+ The script sources `.env` (for `PROJECT`, etc.), runs `oc get ingresses.config.openshift.io cluster` for the apps domain, sets `LLAMA_STACK_HOST` to `llama-stack-demo-route-${PROJECT}.${APPS_DOMAIN}`, then runs the example with any arguments you pass.
203
+
204
+ ## Development
205
+
206
+ Uses [uv](https://docs.astral.sh/uv/) for the venv and running tools. From the cohorte directory:
207
+
208
+ - **Create venv and install deps:** `make install-dev` (or `uv sync --extra dev`)
209
+ - **Lock dependencies:** `make lock` (or `uv lock`)
210
+ - **Lint:** `make lint` (ruff via `uv run`)
211
+ - **Format:** `make format`
212
+ - **Tests:** `make test` (or `uv run pytest tests`)
213
+ - **Coverage:** `make coverage`
214
+ - **Build:** `make build` (or `uv run python -m build`)
215
+
216
+ ## License
217
+
218
+ Apache-2.0. See [LICENSE](LICENSE).
@@ -0,0 +1,75 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "portazgo"
7
+ version = "0.1.0"
8
+ description = "Pluggable agent SDK to talk to Llama Stack using a cohort of agents (default Llama Stack Responses API or LangGraph)"
9
+ readme = "README.md"
10
+ license = { text = "Apache-2.0" }
11
+ requires-python = ">=3.12"
12
+ authors = [
13
+ { name = "Alpha Hack Program" }
14
+ ]
15
+ keywords = ["rag", "ragas", "llama-stack", "langgraph", "agent"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: Apache Software License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.12",
22
+ ]
23
+ dependencies = [
24
+ "httpx",
25
+ # Pin to 0.3.x for compatibility with Llama Stack server 0.3.5.x (426 if client is 0.5.x).
26
+ "llama-stack-client>=0.3.5,<0.4",
27
+ ]
28
+
29
+ [project.optional-dependencies]
30
+ dev = [
31
+ "pytest>=7.0",
32
+ "pytest-cov>=4.0",
33
+ "ruff>=0.1.0",
34
+ "build>=1.0",
35
+ "python-dotenv>=1.0",
36
+ ]
37
+ langgraph = [
38
+ "langgraph>=0.2",
39
+ ]
40
+
41
+ [project.urls]
42
+ Repository = "https://github.com/alpha-hack-program/llama-stack-demo"
43
+ Documentation = "https://github.com/alpha-hack-program/llama-stack-demo#readme"
44
+
45
+ [tool.setuptools.packages.find]
46
+ where = ["src"]
47
+
48
+ [tool.uv]
49
+ # Use: uv sync --extra dev for development. Optional: uv lock for lockfile.
50
+
51
+ [[tool.uv.index]]
52
+ name = "testpypi"
53
+ url = "https://test.pypi.org/simple/"
54
+ publish-url = "https://test.pypi.org/legacy/"
55
+ explicit = true
56
+
57
+ [tool.ruff]
58
+ line-length = 100
59
+ target-version = "py310"
60
+
61
+ [tool.ruff.lint]
62
+ select = ["E", "F", "I", "N", "W"]
63
+ ignore = ["E501"]
64
+
65
+ [tool.pytest.ini_options]
66
+ testpaths = ["tests"]
67
+ pythonpath = ["src"]
68
+ addopts = "-v --tb=short"
69
+
70
+ [tool.coverage.run]
71
+ source = ["src/cohorte"]
72
+ branch = true
73
+
74
+ [tool.coverage.report]
75
+ exclude_lines = ["raise NotImplementedError", "if TYPE_CHECKING:"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,36 @@
1
+ # Copyright 2025 IBM, Red Hat
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # SPDX-License-Identifier: Apache-2.0
15
+
16
+ """
17
+ cohorte: Pluggable agent backends for RAGAS dataset generation.
18
+
19
+ Use Agent(type="default") for Llama Stack Responses API, or Agent(type="lang-graph")
20
+ for a future LangGraph-based implementation.
21
+ """
22
+
23
+ from cohorte.agent import Agent, AgentType
24
+ from cohorte.chats import ChatMessage, format_history_as_prefix
25
+ from cohorte.utils import extract_tool_calls, serialize_for_json, strip_think_blocks
26
+
27
+ __all__ = [
28
+ "Agent",
29
+ "AgentType",
30
+ "ChatMessage",
31
+ "extract_tool_calls",
32
+ "format_history_as_prefix",
33
+ "serialize_for_json",
34
+ "strip_think_blocks",
35
+ ]
36
+ __version__ = "0.1.0"