langchain-trigger-server 0.2.7__tar.gz → 0.2.8__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.
Potentially problematic release.
This version of langchain-trigger-server might be problematic. Click here for more details.
- langchain_trigger_server-0.2.8/.github/workflows/release.yml +124 -0
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/PKG-INFO +1 -1
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/langchain_triggers/app.py +7 -1
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/langchain_triggers/core.py +7 -0
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/pyproject.toml +1 -1
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/tests/unit/test_trigger_server_api.py +95 -1
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/uv.lock +1 -1
- langchain_trigger_server-0.2.8/version_comparison.txt +1 -0
- langchain_trigger_server-0.2.7/.github/workflows/release.yml +0 -38
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/.github/actions/uv_setup/action.yml +0 -0
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/.github/workflows/_lint.yml +0 -0
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/.github/workflows/_test.yml +0 -0
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/.github/workflows/ci.yml +0 -0
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/.gitignore +0 -0
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/Makefile +0 -0
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/README.md +0 -0
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/langchain_triggers/__init__.py +0 -0
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/langchain_triggers/auth/__init__.py +0 -0
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/langchain_triggers/auth/slack_hmac.py +0 -0
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/langchain_triggers/cron_manager.py +0 -0
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/langchain_triggers/database/__init__.py +0 -0
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/langchain_triggers/database/interface.py +0 -0
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/langchain_triggers/database/supabase.py +0 -0
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/langchain_triggers/decorators.py +0 -0
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/langchain_triggers/triggers/__init__.py +0 -0
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/langchain_triggers/triggers/cron_trigger.py +0 -0
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/tests/__init__.py +0 -0
- {langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/tests/unit/__init__.py +0 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
name: Build and publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
release:
|
|
7
|
+
types: [published]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
env:
|
|
11
|
+
PYTHON_VERSION: "3.11"
|
|
12
|
+
UV_FROZEN: "true"
|
|
13
|
+
UV_NO_SYNC: "true"
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
# First, run lint and test to ensure quality
|
|
17
|
+
lint-and-test:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
strategy:
|
|
20
|
+
matrix:
|
|
21
|
+
python-version: ["3.11", "3.12"]
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
|
|
25
|
+
- name: Set up Python + uv
|
|
26
|
+
uses: "./.github/actions/uv_setup"
|
|
27
|
+
with:
|
|
28
|
+
python-version: ${{ matrix.python-version }}
|
|
29
|
+
|
|
30
|
+
- name: Install dependencies
|
|
31
|
+
run: uv sync --group dev
|
|
32
|
+
|
|
33
|
+
- name: Run lint
|
|
34
|
+
run: uv run ruff check .
|
|
35
|
+
|
|
36
|
+
- name: Run tests
|
|
37
|
+
run: uv run pytest tests/
|
|
38
|
+
|
|
39
|
+
# Check version and publish package
|
|
40
|
+
build-and-publish:
|
|
41
|
+
needs: lint-and-test
|
|
42
|
+
name: Build and publish Python distribution to PyPI
|
|
43
|
+
runs-on: ubuntu-latest
|
|
44
|
+
environment:
|
|
45
|
+
name: pypi
|
|
46
|
+
url: https://pypi.org/p/langchain-trigger-server
|
|
47
|
+
permissions:
|
|
48
|
+
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
|
|
49
|
+
contents: read # Required for private repository access
|
|
50
|
+
|
|
51
|
+
steps:
|
|
52
|
+
- uses: actions/checkout@v4
|
|
53
|
+
with:
|
|
54
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
55
|
+
|
|
56
|
+
- name: Set up Python + uv
|
|
57
|
+
uses: "./.github/actions/uv_setup"
|
|
58
|
+
with:
|
|
59
|
+
python-version: ${{ env.PYTHON_VERSION }}
|
|
60
|
+
|
|
61
|
+
- name: Get current version from pyproject.toml
|
|
62
|
+
id: get-version
|
|
63
|
+
run: |
|
|
64
|
+
VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
|
|
65
|
+
echo "current_version=$VERSION" >> $GITHUB_OUTPUT
|
|
66
|
+
echo "Current version: $VERSION"
|
|
67
|
+
|
|
68
|
+
- name: Get latest PyPI version
|
|
69
|
+
id: get-pypi-version
|
|
70
|
+
run: |
|
|
71
|
+
PYPI_VERSION=$(python -c "
|
|
72
|
+
import json, urllib.request, urllib.error
|
|
73
|
+
try:
|
|
74
|
+
with urllib.request.urlopen('https://pypi.org/pypi/langchain-trigger-server/json') as response:
|
|
75
|
+
data = json.loads(response.read())
|
|
76
|
+
print(data['info']['version'])
|
|
77
|
+
except urllib.error.HTTPError as e:
|
|
78
|
+
if e.code == 404:
|
|
79
|
+
print('0.0.0') # Package doesn't exist yet
|
|
80
|
+
else:
|
|
81
|
+
raise
|
|
82
|
+
")
|
|
83
|
+
echo "pypi_version=$PYPI_VERSION" >> $GITHUB_OUTPUT
|
|
84
|
+
echo "PyPI version: $PYPI_VERSION"
|
|
85
|
+
|
|
86
|
+
- name: Compare versions
|
|
87
|
+
id: compare-versions
|
|
88
|
+
run: |
|
|
89
|
+
python -c "
|
|
90
|
+
from distutils.version import LooseVersion
|
|
91
|
+
current = LooseVersion('${{ steps.get-version.outputs.current_version }}')
|
|
92
|
+
pypi = LooseVersion('${{ steps.get-pypi-version.outputs.pypi_version }}')
|
|
93
|
+
should_publish = current > pypi
|
|
94
|
+
print(f'Current: {current}, PyPI: {pypi}, Should publish: {should_publish}')
|
|
95
|
+
" > version_comparison.txt
|
|
96
|
+
cat version_comparison.txt
|
|
97
|
+
|
|
98
|
+
SHOULD_PUBLISH=$(python -c "
|
|
99
|
+
from distutils.version import LooseVersion
|
|
100
|
+
current = LooseVersion('${{ steps.get-version.outputs.current_version }}')
|
|
101
|
+
pypi = LooseVersion('${{ steps.get-pypi-version.outputs.pypi_version }}')
|
|
102
|
+
print('true' if current > pypi else 'false')
|
|
103
|
+
")
|
|
104
|
+
echo "should_publish=$SHOULD_PUBLISH" >> $GITHUB_OUTPUT
|
|
105
|
+
|
|
106
|
+
- name: Build package
|
|
107
|
+
if: steps.compare-versions.outputs.should_publish == 'true'
|
|
108
|
+
run: uv build
|
|
109
|
+
|
|
110
|
+
- name: Publish package distributions to PyPI
|
|
111
|
+
if: steps.compare-versions.outputs.should_publish == 'true'
|
|
112
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
113
|
+
with:
|
|
114
|
+
verbose: true
|
|
115
|
+
print-hash: true
|
|
116
|
+
attestations: false
|
|
117
|
+
|
|
118
|
+
- name: Create summary
|
|
119
|
+
run: |
|
|
120
|
+
if [ "${{ steps.compare-versions.outputs.should_publish }}" = "true" ]; then
|
|
121
|
+
echo "✅ Published langchain-trigger-server v${{ steps.get-version.outputs.current_version }} to PyPI" >> $GITHUB_STEP_SUMMARY
|
|
122
|
+
else
|
|
123
|
+
echo "⏭️ Skipped langchain-trigger-server: v${{ steps.get-version.outputs.current_version }} not newer than PyPI v${{ steps.get-pypi-version.outputs.pypi_version }}" >> $GITHUB_STEP_SUMMARY
|
|
124
|
+
fi
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: langchain-trigger-server
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.8
|
|
4
4
|
Summary: Generic event-driven triggers framework
|
|
5
5
|
Project-URL: Homepage, https://github.com/langchain-ai/open-agent-platform
|
|
6
6
|
Project-URL: Repository, https://github.com/langchain-ai/open-agent-platform
|
|
@@ -323,6 +323,7 @@ class TriggerServer:
|
|
|
323
323
|
raise HTTPException(
|
|
324
324
|
status_code=400, detail=f"Unknown trigger type: {trigger_id}"
|
|
325
325
|
)
|
|
326
|
+
client_metadata = payload.pop("metadata", None)
|
|
326
327
|
|
|
327
328
|
# Parse payload into registration model first
|
|
328
329
|
try:
|
|
@@ -368,11 +369,16 @@ class TriggerServer:
|
|
|
368
369
|
|
|
369
370
|
resource_dict = registration_instance.model_dump()
|
|
370
371
|
|
|
372
|
+
merged_metadata = {}
|
|
373
|
+
if client_metadata:
|
|
374
|
+
merged_metadata["client_metadata"] = client_metadata
|
|
375
|
+
merged_metadata.update(result.metadata)
|
|
376
|
+
|
|
371
377
|
registration = await self.database.create_trigger_registration(
|
|
372
378
|
user_id=user_id,
|
|
373
379
|
template_id=trigger.id,
|
|
374
380
|
resource=resource_dict,
|
|
375
|
-
metadata=
|
|
381
|
+
metadata=merged_metadata,
|
|
376
382
|
)
|
|
377
383
|
|
|
378
384
|
if not registration:
|
{langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/langchain_triggers/core.py
RENAMED
|
@@ -88,6 +88,13 @@ class TriggerRegistrationResult(BaseModel):
|
|
|
88
88
|
"""Validate that required fields are provided based on create_registration."""
|
|
89
89
|
if self.create_registration and not self.metadata:
|
|
90
90
|
self.metadata = {} # Allow empty metadata for create_registration=True
|
|
91
|
+
|
|
92
|
+
if "client_metadata" in self.metadata:
|
|
93
|
+
raise ValueError(
|
|
94
|
+
"The 'client_metadata' key is reserved for client-provided metadata. "
|
|
95
|
+
"Registration handlers must not use this key in their metadata."
|
|
96
|
+
)
|
|
97
|
+
|
|
91
98
|
if not self.create_registration and (
|
|
92
99
|
not self.response_body or not self.status_code
|
|
93
100
|
):
|
|
@@ -6,11 +6,11 @@ from unittest.mock import AsyncMock, patch
|
|
|
6
6
|
import pytest
|
|
7
7
|
import pytest_asyncio
|
|
8
8
|
from httpx import ASGITransport, AsyncClient
|
|
9
|
+
from pydantic import BaseModel
|
|
9
10
|
|
|
10
11
|
from langchain_triggers import TriggerServer
|
|
11
12
|
|
|
12
13
|
|
|
13
|
-
# Mock auth handler that returns a valid user
|
|
14
14
|
async def mock_auth_handler(request_body, headers):
|
|
15
15
|
"""Mock authentication handler for testing."""
|
|
16
16
|
auth_header = headers.get("authorization", "")
|
|
@@ -21,6 +21,17 @@ async def mock_auth_handler(request_body, headers):
|
|
|
21
21
|
return {"identity": f"test_user_{token}"}
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
class TestRegistration(BaseModel):
|
|
25
|
+
"""Simple registration model for testing."""
|
|
26
|
+
|
|
27
|
+
name: str
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
async def dummy_trigger_handler(payload, query_params, database, auth_client):
|
|
31
|
+
"""Dummy trigger handler for test triggers."""
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
|
|
24
35
|
# Mock database class
|
|
25
36
|
class MockDatabase:
|
|
26
37
|
"""Mock database for testing."""
|
|
@@ -354,3 +365,86 @@ async def test_user_isolation(trigger_server):
|
|
|
354
365
|
data = response.json()
|
|
355
366
|
assert len(data["data"]) == 1
|
|
356
367
|
assert data["data"][0]["user_id"] == "test_user_token2"
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
@pytest.mark.asyncio
|
|
371
|
+
async def test_client_metadata_storage(trigger_server):
|
|
372
|
+
"""Test that client-provided metadata is stored as client_metadata."""
|
|
373
|
+
from langchain_triggers import TriggerRegistrationResult, TriggerTemplate
|
|
374
|
+
|
|
375
|
+
async def test_registration_handler(user_id, auth_client, registration):
|
|
376
|
+
return TriggerRegistrationResult(metadata={"handler_data": "from_handler"})
|
|
377
|
+
|
|
378
|
+
test_trigger = TriggerTemplate(
|
|
379
|
+
id="test_metadata_trigger",
|
|
380
|
+
provider="test",
|
|
381
|
+
name="Test Metadata",
|
|
382
|
+
description="Tests metadata storage",
|
|
383
|
+
registration_model=TestRegistration,
|
|
384
|
+
registration_handler=test_registration_handler,
|
|
385
|
+
trigger_handler=dummy_trigger_handler,
|
|
386
|
+
)
|
|
387
|
+
trigger_server.add_trigger(test_trigger)
|
|
388
|
+
|
|
389
|
+
transport = ASGITransport(app=trigger_server.app, raise_app_exceptions=True)
|
|
390
|
+
async with AsyncClient(base_url="http://localhost", transport=transport) as client:
|
|
391
|
+
response = await client.post(
|
|
392
|
+
"/v1/triggers/registrations",
|
|
393
|
+
headers={"Authorization": "Bearer token1"},
|
|
394
|
+
json={
|
|
395
|
+
"type": "test_metadata_trigger",
|
|
396
|
+
"name": "test",
|
|
397
|
+
"metadata": {"tenant_id": "org-123"},
|
|
398
|
+
},
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
assert response.status_code == 200
|
|
402
|
+
data = response.json()
|
|
403
|
+
assert data["success"] is True
|
|
404
|
+
|
|
405
|
+
# Verify metadata in API response
|
|
406
|
+
registration_id = data["data"]["id"]
|
|
407
|
+
metadata = data["data"]["metadata"]
|
|
408
|
+
assert metadata["client_metadata"]["tenant_id"] == "org-123"
|
|
409
|
+
assert metadata["handler_data"] == "from_handler"
|
|
410
|
+
|
|
411
|
+
# Verify metadata persisted in database
|
|
412
|
+
db_registration = await trigger_server.database.get_trigger_registration(
|
|
413
|
+
registration_id, user_id="test_user_token1"
|
|
414
|
+
)
|
|
415
|
+
assert db_registration is not None
|
|
416
|
+
db_metadata = db_registration["metadata"]
|
|
417
|
+
assert db_metadata["client_metadata"]["tenant_id"] == "org-123"
|
|
418
|
+
assert db_metadata["handler_data"] == "from_handler"
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
@pytest.mark.asyncio
|
|
422
|
+
async def test_handler_cannot_use_client_metadata_key(trigger_server):
|
|
423
|
+
"""Test that handlers cannot use the reserved client_metadata key."""
|
|
424
|
+
from langchain_triggers import TriggerRegistrationResult, TriggerTemplate
|
|
425
|
+
|
|
426
|
+
async def bad_handler(user_id, auth_client, registration):
|
|
427
|
+
# Handler tries to use reserved key
|
|
428
|
+
return TriggerRegistrationResult(metadata={"client_metadata": "not allowed"})
|
|
429
|
+
|
|
430
|
+
test_trigger = TriggerTemplate(
|
|
431
|
+
id="test_bad_handler",
|
|
432
|
+
provider="test",
|
|
433
|
+
name="Test Bad Handler",
|
|
434
|
+
description="Tests validation",
|
|
435
|
+
registration_model=TestRegistration,
|
|
436
|
+
registration_handler=bad_handler,
|
|
437
|
+
trigger_handler=dummy_trigger_handler,
|
|
438
|
+
)
|
|
439
|
+
trigger_server.add_trigger(test_trigger)
|
|
440
|
+
|
|
441
|
+
transport = ASGITransport(app=trigger_server.app, raise_app_exceptions=True)
|
|
442
|
+
async with AsyncClient(base_url="http://localhost", transport=transport) as client:
|
|
443
|
+
response = await client.post(
|
|
444
|
+
"/v1/triggers/registrations",
|
|
445
|
+
headers={"Authorization": "Bearer token1"},
|
|
446
|
+
json={"type": "test_bad_handler", "name": "test"},
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
assert response.status_code == 500
|
|
450
|
+
assert "client_metadata" in response.json()["detail"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Current: 0.2.8, PyPI: 0.2.7, Should publish: True
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
name: Build and publish to PyPI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
release:
|
|
5
|
-
types: [published]
|
|
6
|
-
workflow_dispatch:
|
|
7
|
-
|
|
8
|
-
jobs:
|
|
9
|
-
build-and-publish:
|
|
10
|
-
name: Build and publish Python distribution to PyPI
|
|
11
|
-
runs-on: ubuntu-latest
|
|
12
|
-
environment:
|
|
13
|
-
name: pypi
|
|
14
|
-
url: https://pypi.org/p/langchain-trigger-server
|
|
15
|
-
permissions:
|
|
16
|
-
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
|
|
17
|
-
contents: read # Required for private repository access
|
|
18
|
-
|
|
19
|
-
steps:
|
|
20
|
-
- uses: actions/checkout@v4
|
|
21
|
-
with:
|
|
22
|
-
token: ${{ secrets.GITHUB_TOKEN }}
|
|
23
|
-
|
|
24
|
-
- name: Set up Python
|
|
25
|
-
uses: actions/setup-python@v4
|
|
26
|
-
with:
|
|
27
|
-
python-version: "3.11"
|
|
28
|
-
|
|
29
|
-
- name: Install build dependencies
|
|
30
|
-
run: |
|
|
31
|
-
python -m pip install --upgrade pip
|
|
32
|
-
python -m pip install build
|
|
33
|
-
|
|
34
|
-
- name: Build package
|
|
35
|
-
run: python -m build
|
|
36
|
-
|
|
37
|
-
- name: Publish package distributions to PyPI
|
|
38
|
-
uses: pypa/gh-action-pypi-publish@release/v1
|
|
File without changes
|
{langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/.github/workflows/_lint.yml
RENAMED
|
File without changes
|
{langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/.github/workflows/_test.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/langchain_triggers/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/langchain_triggers/cron_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{langchain_trigger_server-0.2.7 → langchain_trigger_server-0.2.8}/langchain_triggers/decorators.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|