Skip to content

Commit 4778ea7

Browse files
author
Tom Softreck
committed
update
1 parent 6ed9314 commit 4778ea7

File tree

7 files changed

+143
-28
lines changed

7 files changed

+143
-28
lines changed

Makefile

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,41 @@ version:
9292
git tag -a "v$$(poetry version --short)" -m "Version $$(poetry version --short)"
9393
@echo "✅ Version bumped and tagged. Don't forget to push with tags: git push --follow-tags"
9494

95+
# Helper to get PYPI_TOKEN from files
96+
define get_pypi_token
97+
$(shell \
98+
if [ -f "${HOME}/.pypirc" ]; then \
99+
grep -A 2 '\[pypi\]' "${HOME}/.pypirc" 2>/dev/null | grep 'token = ' | cut -d' ' -f3; \
100+
elif [ -f ".pypirc" ]; then \
101+
grep -A 2 '\[pypi\]' ".pypirc" 2>/dev/null | grep 'token = ' | cut -d' ' -f3; \
102+
elif [ -f ".env" ]; then \
103+
grep '^PYPI_TOKEN=' ".env" 2>/dev/null | cut -d'=' -f2-; \
104+
fi
105+
)
106+
endef
107+
108+
# Export the function to be used in the recipe
109+
PYPI_TOKEN_FROM_FILE := $(call get_pypi_token)
110+
95111
# Publishing
96-
publish: build
97-
@if [ -z "$(PYPI_TOKEN)" ]; then \
98-
echo "Error: Please set PYPI_TOKEN environment variable"; \
112+
publish:
113+
@echo "🔄 Bumping version..."
114+
poetry version patch
115+
@echo "🧹 Cleaning build artifacts..."
116+
@$(MAKE) clean
117+
@echo "🏗️ Building package..."
118+
poetry build
119+
@if [ -z "$(PYPI_TOKEN)" ] && [ -z "$(PYPI_TOKEN_FROM_FILE)" ]; then \
120+
echo "❌ Error: PYPI_TOKEN not found"; \
121+
echo " Please either:"; \
122+
echo " 1. Add token to ~/.pypirc or .pypirc: [pypi]"; \
123+
echo " token = pypi_..."; \
124+
echo " 2. Add PYPI_TOKEN=... to .env file"; \
125+
echo " 3. Run: make publish PYPI_TOKEN=your_token_here"; \
99126
exit 1; \
100127
fi
101-
@echo "🚀 Publishing to PyPI..."
102-
poetry publish --build --username=__token__ --password=$(PYPI_TOKEN)
128+
@echo "🚀 Publishing to PyPI..."; \
129+
poetry publish --username=__token__ --password=$(or $(PYPI_TOKEN),$(PYPI_TOKEN_FROM_FILE))
103130
@echo "✅ Successfully published to PyPI"
104131

105132
# Test publishing
@@ -205,7 +232,7 @@ run-example: setup-env
205232
@echo "🚀 Starting $(EXAMPLE) example..."
206233
@case "$(EXAMPLE)" in \
207234
simple) \
208-
poetry run dialogchain run -c examples/simple_routes.yaml ;; \
235+
poetry run dialogchain run -c examples/simple_config.yaml ;; \
209236
grpc) \
210237
docker-compose -f examples/docker-compose.yml up -d grpc-server && \
211238
poetry run dialogchain run -c examples/grpc_routes.yaml ;; \
@@ -219,6 +246,10 @@ run-example: setup-env
219246
exit 1 ;; \
220247
esac
221248

249+
# Run the simple example with verbose output
250+
run-simple-verbose:
251+
poetry run dialogchain run -c examples/simple_config.yaml --verbose
252+
222253
# View logs for running example
223254
view-logs:
224255
@if [ -z "$(EXAMPLE)" ]; then \

examples/simple_config.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Simple DialogChain Configuration
2+
routes:
3+
- name: hello_world
4+
source:
5+
type: http
6+
endpoint: /hello
7+
method: GET
8+
processors:
9+
- type: transform
10+
template: '{"message": "Hello, World!"}'
11+
sink:
12+
type: http_response
13+
content_type: application/json

my_config.yaml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Camera Processing Routes
2+
routes:
3+
- name: "front_door_camera"
4+
from: "rtsp://{{CAMERA_USER}}:{{CAMERA_PASS}}@{{CAMERA_IP}}/stream1"
5+
processors:
6+
- type: "external"
7+
command: "python -m ultralytics_processor"
8+
input_format: "frame_stream"
9+
output_format: "json"
10+
config:
11+
confidence_threshold: 0.6
12+
target_objects: ["person", "car"]
13+
14+
- type: "filter"
15+
condition: "{{confidence}} > 0.7"
16+
17+
- type: "transform"
18+
template: "Person detected at {{position}} ({{confidence}}%)"
19+
20+
to: "email://{{SMTP_SERVER}}:{{SMTP_PORT}}?user={{SMTP_USER}}&password={{SMTP_PASS}}&to={{ALERT_EMAIL}}"
21+
22+
env_vars:
23+
- CAMERA_USER
24+
- CAMERA_PASS
25+
- CAMERA_IP
26+
- SMTP_SERVER
27+
- SMTP_PORT
28+
- SMTP_USER
29+
- SMTP_PASS
30+
- ALERT_EMAIL

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
44

55
[project]
66
name = "dialogchain"
7-
version = "0.1.3"
7+
version = "0.1.4"
88
description = "DialogChain - A flexible and extensible dialog processing framework"
99
authors = [
1010
{name = "DialogChain Team", email = "team@dialogchain.org"},

tests/integration/test_mock_server.py

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ async def test_mock_server_endpoints(mock_server, config):
9999
assert "echo" in echo_data, f"Response missing 'echo' key: {echo_data}"
100100
# The echo endpoint returns the processed data as a string
101101
assert isinstance(echo_data["echo"], str), f"Expected string response, got {type(echo_data['echo'])}"
102-
assert "Processed: " in echo_data["echo"], f"Expected 'Processed: ' in response: {echo_data['echo']}"
102+
assert "Processed: " in echo_data["echo"], f"Expected 'Processed: ' in response: {echo_data}"
103103

104104
# Test GET /api/events
105105
async with session.get(f"{base_url}/api/events") as response:
@@ -119,12 +119,25 @@ async def __aenter__(self):
119119
async def __aexit__(self, exc_type, exc_val, exc_tb):
120120
pass
121121

122-
async def receive(self):
123-
"""Return test data"""
124-
return [{
122+
def __aiter__(self):
123+
self._data = [{
125124
'data': {'test': 'data'},
126125
'metadata': {'url': 'mock://test', 'status': 200}
127126
}]
127+
self._index = 0
128+
return self
129+
130+
async def __anext__(self):
131+
if self._index >= len(self._data):
132+
raise StopAsyncIteration
133+
result = self._data[self._index]
134+
self._index += 1
135+
return result
136+
137+
# For backward compatibility with the engine
138+
def receive(self):
139+
"""Return self as an async iterator"""
140+
return self
128141

129142
class TestCamelRouterEngine(CamelRouterEngine):
130143
"""Test engine that uses our mock source"""
@@ -158,21 +171,49 @@ async def test_dialogchain_with_mock_server(mock_server, config):
158171
# Initialize our test engine with the config
159172
engine = TestCamelRouterEngine(test_config, verbose=True)
160173

161-
# Test the route processing
162-
async with aiohttp.ClientSession() as session:
163-
# Get data from the mock server to verify it's working
164-
async with session.get(f"http://localhost:{port}/api/data") as response:
165-
assert response.status == 200, f"Failed to GET /api/data: {await response.text()}"
166-
data = await response.json()
167-
assert "data" in data, f"Unexpected response format: {data}"
174+
# Create a mock destination to capture the output
175+
class MockDestination:
176+
def __init__(self):
177+
self.received_messages = []
168178

169-
# Run the route configuration
170-
await engine.run_route_config(test_config['routes'][0])
171-
172-
# Verify the data was processed and sent to the echo endpoint
173-
# We'll check the mock server's echo endpoint to see the processed data
174-
async with session.get(f"http://localhost:{port}/api/echo") as echo_response:
175-
assert echo_response.status == 200, f"Failed to GET /api/echo: {await echo_response.text()}"
176-
echo_data = await echo_response.json()
177-
assert "echo" in echo_data, f"Unexpected response format from echo: {echo_data}"
178-
assert "Processed: " in str(echo_data["echo"]), f"Expected 'Processed: ' in response: {echo_data}"
179+
async def __aenter__(self):
180+
return self
181+
182+
async def __aexit__(self, exc_type, exc_val, exc_tb):
183+
pass
184+
185+
async def send(self, message):
186+
self.received_messages.append(message)
187+
return [{"status": "success"}]
188+
189+
# Create a mock destination
190+
mock_dest = MockDestination()
191+
192+
# Patch the engine's create_destination method to return our mock
193+
original_create_dest = engine.create_destination
194+
195+
def patched_create_dest(uri):
196+
if uri == "mock://destination":
197+
return mock_dest
198+
return original_create_dest(uri)
199+
200+
engine.create_destination = patched_create_dest
201+
202+
# Run the route configuration
203+
await engine.run_route_config(test_config['routes'][0])
204+
205+
# Verify the destination received the processed message
206+
assert len(mock_dest.received_messages) > 0, "No messages were sent to the destination"
207+
message = mock_dest.received_messages[0]
208+
209+
# The message should have the original data and metadata, plus the processed message
210+
assert "data" in message, f"Message missing 'data' key: {message}"
211+
assert "metadata" in message, f"Message missing 'metadata' key: {message}"
212+
assert "message" in message, f"Message missing 'message' key: {message}"
213+
214+
# Check the processed message
215+
assert message["message"] == "Processed: data", f"Unexpected processed message: {message}"
216+
217+
# Check the original data is preserved
218+
assert message["data"] == {"test": "data"}, f"Unexpected data: {message['data']}"
219+
assert message["metadata"]["url"] == "mock://test", f"Unexpected URL: {message['metadata']['url']}"

0 commit comments

Comments
 (0)