amigo_sdk 0.100.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.
Files changed (45) hide show
  1. amigo_sdk-0.100.0/.github/workflows/auto-release.yml +183 -0
  2. amigo_sdk-0.100.0/.github/workflows/release.yml +215 -0
  3. amigo_sdk-0.100.0/.github/workflows/test.yml +86 -0
  4. amigo_sdk-0.100.0/.gitignore +18 -0
  5. amigo_sdk-0.100.0/CONTRIBUTING.md +137 -0
  6. amigo_sdk-0.100.0/LICENSE +21 -0
  7. amigo_sdk-0.100.0/PKG-INFO +264 -0
  8. amigo_sdk-0.100.0/README.md +240 -0
  9. amigo_sdk-0.100.0/pyproject.toml +146 -0
  10. amigo_sdk-0.100.0/scripts/__init__.py +0 -0
  11. amigo_sdk-0.100.0/scripts/aliases.json +5 -0
  12. amigo_sdk-0.100.0/scripts/check.py +102 -0
  13. amigo_sdk-0.100.0/scripts/gen_models.py +122 -0
  14. amigo_sdk-0.100.0/specs/openapi-baseline.json +1 -0
  15. amigo_sdk-0.100.0/src/amigo_sdk/__init__.py +4 -0
  16. amigo_sdk-0.100.0/src/amigo_sdk/_retry_utils.py +69 -0
  17. amigo_sdk-0.100.0/src/amigo_sdk/auth.py +48 -0
  18. amigo_sdk-0.100.0/src/amigo_sdk/config.py +48 -0
  19. amigo_sdk-0.100.0/src/amigo_sdk/errors.py +163 -0
  20. amigo_sdk-0.100.0/src/amigo_sdk/generated/model.py +15903 -0
  21. amigo_sdk-0.100.0/src/amigo_sdk/http_client.py +380 -0
  22. amigo_sdk-0.100.0/src/amigo_sdk/models.py +1 -0
  23. amigo_sdk-0.100.0/src/amigo_sdk/resources/conversation.py +361 -0
  24. amigo_sdk-0.100.0/src/amigo_sdk/resources/organization.py +34 -0
  25. amigo_sdk-0.100.0/src/amigo_sdk/resources/service.py +44 -0
  26. amigo_sdk-0.100.0/src/amigo_sdk/resources/user.py +111 -0
  27. amigo_sdk-0.100.0/src/amigo_sdk/sdk_client.py +187 -0
  28. amigo_sdk-0.100.0/tests/__init__.py +1 -0
  29. amigo_sdk-0.100.0/tests/conftest.py +20 -0
  30. amigo_sdk-0.100.0/tests/integration/test_conversation_integration.py +349 -0
  31. amigo_sdk-0.100.0/tests/integration/test_organization_integration.py +179 -0
  32. amigo_sdk-0.100.0/tests/integration/test_user_integration.py +208 -0
  33. amigo_sdk-0.100.0/tests/resources/__init__.py +1 -0
  34. amigo_sdk-0.100.0/tests/resources/helpers.py +363 -0
  35. amigo_sdk-0.100.0/tests/resources/test_conversation.py +889 -0
  36. amigo_sdk-0.100.0/tests/resources/test_organization.py +95 -0
  37. amigo_sdk-0.100.0/tests/resources/test_service.py +90 -0
  38. amigo_sdk-0.100.0/tests/resources/test_user.py +271 -0
  39. amigo_sdk-0.100.0/tests/test_auth.py +172 -0
  40. amigo_sdk-0.100.0/tests/test_config.py +132 -0
  41. amigo_sdk-0.100.0/tests/test_errors.py +117 -0
  42. amigo_sdk-0.100.0/tests/test_http_client.py +743 -0
  43. amigo_sdk-0.100.0/tests/test_retry_utils.py +86 -0
  44. amigo_sdk-0.100.0/tests/test_sdk_client.py +128 -0
  45. amigo_sdk-0.100.0/uv.lock +1318 -0
@@ -0,0 +1,183 @@
1
+ name: Auto Release (Backend API changes)
2
+
3
+ on:
4
+ repository_dispatch:
5
+ types: [openapi-updated]
6
+
7
+ permissions:
8
+ contents: write
9
+
10
+ jobs:
11
+ detect:
12
+ name: Detect OpenAPI change
13
+ runs-on: blacksmith-4vcpu-ubuntu-2404
14
+ outputs:
15
+ changed: ${{ steps.diff.outputs.changed }}
16
+ spec_url: ${{ steps.spec.outputs.spec_url }}
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Ensure jq
21
+ run: sudo apt-get update && sudo apt-get install -y jq
22
+
23
+ - name: Resolve spec URL
24
+ id: spec
25
+ run: |
26
+ SPEC_URL="${{ github.event.client_payload.spec_url }}"
27
+ if [ -z "$SPEC_URL" ]; then
28
+ SPEC_URL="https://api.amigo.ai/v1/openapi.json"
29
+ fi
30
+ echo "spec_url=$SPEC_URL" >> $GITHUB_OUTPUT
31
+
32
+ - name: Fetch current production spec
33
+ run: |
34
+ mkdir -p specs
35
+ curl -fSL -o specs/openapi-new.json "${{ steps.spec.outputs.spec_url }}"
36
+
37
+ - name: Check baseline presence
38
+ id: baseline
39
+ run: |
40
+ mkdir -p specs
41
+ if [ ! -f specs/openapi-baseline.json ]; then
42
+ echo "missing=true" >> $GITHUB_OUTPUT
43
+ else
44
+ echo "missing=false" >> $GITHUB_OUTPUT
45
+ fi
46
+
47
+ - name: Prepare baseline
48
+ run: |
49
+ mkdir -p specs
50
+ if [ ! -f specs/openapi-baseline.json ]; then
51
+ cp specs/openapi-new.json specs/openapi-baseline.json
52
+ fi
53
+
54
+ - name: Diagnostics (spec and baseline)
55
+ run: |
56
+ echo "Resolved spec_url: ${{ steps.spec.outputs.spec_url }}"
57
+ echo "Baseline missing: ${{ steps.baseline.outputs.missing }}"
58
+ echo "jq version: $(jq --version)"
59
+ jq -S '
60
+ def sort_deep:
61
+ if type == "array" then
62
+ map(sort_deep) | sort_by(tostring)
63
+ elif type == "object" then
64
+ with_entries(.value |= sort_deep)
65
+ else . end;
66
+ (del(.info) | del(.servers)) | sort_deep
67
+ ' specs/openapi-baseline.json > baseline.norm.json || true
68
+ jq -S '
69
+ def sort_deep:
70
+ if type == "array" then
71
+ map(sort_deep) | sort_by(tostring)
72
+ elif type == "object" then
73
+ with_entries(.value |= sort_deep)
74
+ else . end;
75
+ (del(.info) | del(.servers)) | sort_deep
76
+ ' specs/openapi-new.json > new.norm.json
77
+ if [ -f baseline.norm.json ]; then
78
+ echo "BASELINE_NORM_SHA=$(sha256sum baseline.norm.json | awk '{print $1}')"
79
+ else
80
+ echo "BASELINE_NORM_SHA=(none)"
81
+ fi
82
+ echo "NEW_NORM_SHA=$(sha256sum new.norm.json | awk '{print $1}')"
83
+ if [ -f baseline.norm.json ]; then
84
+ if diff -q baseline.norm.json new.norm.json >/dev/null 2>&1; then
85
+ echo "Quick diff: identical"
86
+ else
87
+ echo "Quick diff: differs"
88
+ fi
89
+ fi
90
+
91
+ # Section-level hashes and key diffs
92
+ echo "\n== Section-level hashes =="
93
+ if [ -f specs/openapi-baseline.json ]; then
94
+ BASELINE_PATHS_SHA=$(jq -cS '(.paths // {})' specs/openapi-baseline.json | sha256sum | awk '{print $1}')
95
+ BASELINE_SCHEMAS_SHA=$(jq -cS '(.components.schemas // {})' specs/openapi-baseline.json | sha256sum | awk '{print $1}')
96
+ BASELINE_PATHS_COUNT=$(jq -r '(.paths // {}) | keys | length' specs/openapi-baseline.json)
97
+ BASELINE_SCHEMAS_COUNT=$(jq -r '(.components.schemas // {}) | keys | length' specs/openapi-baseline.json)
98
+ else
99
+ BASELINE_PATHS_SHA=(none)
100
+ BASELINE_SCHEMAS_SHA=(none)
101
+ BASELINE_PATHS_COUNT=0
102
+ BASELINE_SCHEMAS_COUNT=0
103
+ fi
104
+ NEW_PATHS_SHA=$(jq -cS '(.paths // {})' specs/openapi-new.json | sha256sum | awk '{print $1}')
105
+ NEW_SCHEMAS_SHA=$(jq -cS '(.components.schemas // {})' specs/openapi-new.json | sha256sum | awk '{print $1}')
106
+ NEW_PATHS_COUNT=$(jq -r '(.paths // {}) | keys | length' specs/openapi-new.json)
107
+ NEW_SCHEMAS_COUNT=$(jq -r '(.components.schemas // {}) | keys | length' specs/openapi-new.json)
108
+ echo "BASELINE paths: count=$BASELINE_PATHS_COUNT sha=$BASELINE_PATHS_SHA"
109
+ echo "NEW paths: count=$NEW_PATHS_COUNT sha=$NEW_PATHS_SHA"
110
+ echo "BASELINE schemas: count=$BASELINE_SCHEMAS_COUNT sha=$BASELINE_SCHEMAS_SHA"
111
+ echo "NEW schemas: count=$NEW_SCHEMAS_COUNT sha=$NEW_SCHEMAS_SHA"
112
+
113
+ echo "\n== Key diffs (paths) =="
114
+ if [ -f specs/openapi-baseline.json ]; then
115
+ jq -r '(.paths // {}) | keys | sort[]' specs/openapi-baseline.json > baseline.paths.txt || true
116
+ else
117
+ : > baseline.paths.txt
118
+ fi
119
+ jq -r '(.paths // {}) | keys | sort[]' specs/openapi-new.json > new.paths.txt
120
+ echo "Removed paths (in baseline, not in new):"; comm -23 baseline.paths.txt new.paths.txt || true
121
+ echo "Added paths (in new, not in baseline):"; comm -13 baseline.paths.txt new.paths.txt || true
122
+
123
+ echo "\n== Key diffs (components.schemas) =="
124
+ if [ -f specs/openapi-baseline.json ]; then
125
+ jq -r '(.components.schemas // {}) | keys | sort[]' specs/openapi-baseline.json > baseline.schemas.txt || true
126
+ else
127
+ : > baseline.schemas.txt
128
+ fi
129
+ jq -r '(.components.schemas // {}) | keys | sort[]' specs/openapi-new.json > new.schemas.txt
130
+ echo "Removed schemas (in baseline, not in new):"; comm -23 baseline.schemas.txt new.schemas.txt || true
131
+ echo "Added schemas (in new, not in baseline):"; comm -13 baseline.schemas.txt new.schemas.txt || true
132
+
133
+ - name: Compare normalized specs
134
+ id: diff
135
+ run: |
136
+ if [ "${{ steps.baseline.outputs.missing }}" = "true" ]; then
137
+ echo "changed=true" >> $GITHUB_OUTPUT
138
+ exit 0
139
+ fi
140
+ jq -S '
141
+ def sort_deep:
142
+ if type == "array" then
143
+ map(sort_deep) | sort_by(tostring)
144
+ elif type == "object" then
145
+ with_entries(.value |= sort_deep)
146
+ else . end;
147
+ (del(.info) | del(.servers)) | sort_deep
148
+ ' specs/openapi-baseline.json > baseline.norm.json
149
+ jq -S '
150
+ def sort_deep:
151
+ if type == "array" then
152
+ map(sort_deep) | sort_by(tostring)
153
+ elif type == "object" then
154
+ with_entries(.value |= sort_deep)
155
+ else . end;
156
+ (del(.info) | del(.servers)) | sort_deep
157
+ ' specs/openapi-new.json > new.norm.json
158
+ if diff -q baseline.norm.json new.norm.json >/dev/null 2>&1; then
159
+ echo "changed=false" >> $GITHUB_OUTPUT
160
+ else
161
+ echo "changed=true" >> $GITHUB_OUTPUT
162
+ fi
163
+
164
+ - name: Upload normalized specs
165
+ if: always()
166
+ uses: actions/upload-artifact@v4
167
+ with:
168
+ name: normalized-specs
169
+ path: |
170
+ baseline.norm.json
171
+ new.norm.json
172
+ retention-days: 7
173
+
174
+ release-on-dispatch:
175
+ name: Trigger SDK release (minor)
176
+ needs: detect
177
+ if: needs.detect.outputs.changed == 'true'
178
+ uses: ./.github/workflows/release.yml
179
+ with:
180
+ version_type: minor
181
+ dry_run: ${{ github.event.client_payload.dry_run == true }}
182
+ spec_url: ${{ needs.detect.outputs.spec_url }}
183
+ secrets: inherit
@@ -0,0 +1,215 @@
1
+ name: Release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ version_type:
7
+ description: "Version increment type"
8
+ required: true
9
+ default: "patch"
10
+ type: choice
11
+ options:
12
+ - patch
13
+ - minor
14
+ - major
15
+ dry_run:
16
+ description: "Dry run (skip upload)"
17
+ required: false
18
+ default: false
19
+ type: boolean
20
+ workflow_call:
21
+ inputs:
22
+ version_type:
23
+ description: "Version increment type (patch|minor|major)"
24
+ required: true
25
+ type: string
26
+ default: "patch"
27
+ dry_run:
28
+ description: "Dry run (skip upload)"
29
+ required: false
30
+ type: boolean
31
+ default: false
32
+ spec_url:
33
+ description: "OpenAPI spec URL to use for generation"
34
+ required: false
35
+ type: string
36
+ default: ""
37
+
38
+ permissions:
39
+ contents: write
40
+
41
+ jobs:
42
+ # Reuse the existing test workflow
43
+ test:
44
+ uses: ./.github/workflows/test.yml
45
+ with:
46
+ skip_codecov: true
47
+ secrets: inherit
48
+
49
+ release:
50
+ runs-on: blacksmith-4vcpu-ubuntu-2404
51
+ needs: test
52
+
53
+ steps:
54
+ - name: Create GitHub App token (release-bot)
55
+ id: app-token
56
+ uses: actions/create-github-app-token@v2
57
+ with:
58
+ app-id: ${{ secrets.RELEASE_BOT_APP_ID }}
59
+ private-key: ${{ secrets.RELEASE_BOT_PRIVATE_KEY }}
60
+
61
+ - name: Checkout code
62
+ uses: actions/checkout@v4
63
+ with:
64
+ # Fetch full history for version tagging
65
+ fetch-depth: 0
66
+ # Use a token that can push tags and create releases
67
+ token: ${{ steps.app-token.outputs.token }}
68
+
69
+ - name: Ensure jq
70
+ run: sudo apt-get update && sudo apt-get install -y jq
71
+
72
+ - name: Fetch OpenAPI spec
73
+ id: fetch_spec
74
+ run: |
75
+ SPEC_URL="${{ inputs.spec_url }}"
76
+ if [ -z "$SPEC_URL" ]; then
77
+ SPEC_URL="https://api.amigo.ai/v1/openapi.json"
78
+ fi
79
+ mkdir -p specs
80
+ curl -fSL -o specs/openapi-new.json "$SPEC_URL"
81
+ cp specs/openapi-new.json specs/openapi-baseline.json
82
+
83
+ # Generator always fetches remote; no local spec path needed
84
+
85
+ - name: Setup Python
86
+ uses: actions/setup-python@v5
87
+ with:
88
+ python-version: "3.13"
89
+ cache: "pip"
90
+
91
+ - name: Install dependencies
92
+ run: |
93
+ python -m pip install --upgrade pip
94
+ pip install -e ".[dev]"
95
+ pip install hatch
96
+
97
+ - name: Configure git
98
+ run: |
99
+ git config --global user.name "github-actions[bot]"
100
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
101
+
102
+ - name: Get current version
103
+ id: current_version
104
+ run: |
105
+ CURRENT_VERSION=$(hatch version)
106
+ echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
107
+ echo "Current version: $CURRENT_VERSION"
108
+
109
+ - name: Generate models
110
+ run: |
111
+ echo "🔄 Generating models from API spec..."
112
+ python -m scripts.gen_models
113
+ echo "✅ Models generated successfully"
114
+
115
+ - name: Increment version
116
+ id: new_version
117
+ run: |
118
+ echo "🔄 Incrementing ${{ inputs.version_type }} version..."
119
+ hatch version ${{ inputs.version_type }}
120
+ NEW_VERSION=$(hatch version)
121
+ echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
122
+ echo "✅ Version updated: ${{ steps.current_version.outputs.version }} → $NEW_VERSION"
123
+
124
+ - name: Build package
125
+ run: |
126
+ echo "🔄 Building package..."
127
+ # Clean any existing build artifacts
128
+ rm -rf dist/
129
+ hatch build
130
+ echo "✅ Package built successfully"
131
+ ls -la dist/
132
+
133
+ - name: Upload to PyPI
134
+ if: ${{ !inputs.dry_run }}
135
+ env:
136
+ HATCH_INDEX_USER: __token__
137
+ HATCH_INDEX_AUTH: ${{ secrets.PYPI_API_TOKEN }}
138
+ run: |
139
+ echo "🔄 Uploading to PyPI..."
140
+ hatch publish --repo https://upload.pypi.org/legacy/
141
+ echo "✅ Package uploaded to PyPI"
142
+
143
+ - name: Commit version bump
144
+ if: ${{ !inputs.dry_run }}
145
+ run: |
146
+ git add -A
147
+ git commit -m "Bump version to ${{ steps.new_version.outputs.version }}"
148
+ git push origin HEAD
149
+
150
+ - name: Create and push tag
151
+ if: ${{ !inputs.dry_run }}
152
+ run: |
153
+ git tag -a "v${{ steps.new_version.outputs.version }}" -m "Release v${{ steps.new_version.outputs.version }}"
154
+ git push origin "v${{ steps.new_version.outputs.version }}"
155
+
156
+ - name: Create GitHub Release
157
+ if: ${{ !inputs.dry_run }}
158
+ uses: actions/create-release@v1
159
+ env:
160
+ GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
161
+ with:
162
+ tag_name: "v${{ steps.new_version.outputs.version }}"
163
+ release_name: "Release v${{ steps.new_version.outputs.version }}"
164
+ body: |
165
+ ## Changes in v${{ steps.new_version.outputs.version }}
166
+
167
+ This release was automatically created by the GitHub Actions release workflow.
168
+
169
+ ### Package Information
170
+ - **Version**: ${{ steps.new_version.outputs.version }}
171
+ - **Target**: PyPI
172
+ - **Type**: ${{ inputs.version_type }} release
173
+
174
+ ### Links
175
+ ${{ format('- [View on PyPI](https://pypi.org/project/amigo_sdk/{0}/)', steps.new_version.outputs.version) }}
176
+
177
+ ### Installation
178
+ ```bash
179
+ pip install amigo_sdk
180
+ ```
181
+ draft: false
182
+
183
+ - name: Upload build artifacts
184
+ if: always()
185
+ uses: actions/upload-artifact@v4
186
+ with:
187
+ name: dist-${{ steps.new_version.outputs.version }}
188
+ path: dist/
189
+ retention-days: 30
190
+
191
+ - name: Release Summary
192
+ run: |
193
+ echo "## 🎉 Release Summary" >> $GITHUB_STEP_SUMMARY
194
+ echo "" >> $GITHUB_STEP_SUMMARY
195
+ echo "- **Version**: ${{ steps.new_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
196
+ echo "- **Previous**: ${{ steps.current_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
197
+ echo "- **Type**: ${{ inputs.version_type }} release" >> $GITHUB_STEP_SUMMARY
198
+ echo "- **Target**: PyPI" >> $GITHUB_STEP_SUMMARY
199
+ echo "- **Dry Run**: ${{ inputs.dry_run }}" >> $GITHUB_STEP_SUMMARY
200
+ echo "" >> $GITHUB_STEP_SUMMARY
201
+
202
+ if [ "${{ inputs.dry_run }}" = "false" ]; then
203
+ echo "### 📦 Package Links" >> $GITHUB_STEP_SUMMARY
204
+ echo "- [PyPI Package](https://pypi.org/project/amigo_sdk/${{ steps.new_version.outputs.version }}/)" >> $GITHUB_STEP_SUMMARY
205
+ echo "- [GitHub Release](https://github.com/${{ github.repository }}/releases/tag/v${{ steps.new_version.outputs.version }})" >> $GITHUB_STEP_SUMMARY
206
+ echo "" >> $GITHUB_STEP_SUMMARY
207
+ echo "### 🚀 Next Steps" >> $GITHUB_STEP_SUMMARY
208
+ echo "The package has been successfully released and is available for installation!" >> $GITHUB_STEP_SUMMARY
209
+ else
210
+ echo "### 🚫 Dry Run Results" >> $GITHUB_STEP_SUMMARY
211
+ echo "- All steps completed successfully" >> $GITHUB_STEP_SUMMARY
212
+ echo "- Version would be bumped to: ${{ steps.new_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
213
+ echo "- Package built and ready for upload" >> $GITHUB_STEP_SUMMARY
214
+ echo "- No commits, tags, or uploads were made" >> $GITHUB_STEP_SUMMARY
215
+ fi
@@ -0,0 +1,86 @@
1
+ name: Test
2
+
3
+ permissions:
4
+ contents: read
5
+
6
+ on:
7
+ push:
8
+ branches: [main]
9
+ pull_request:
10
+ branches: [main]
11
+ workflow_call:
12
+ inputs:
13
+ skip_codecov:
14
+ description: "Skip Codecov upload"
15
+ required: false
16
+ default: true
17
+ type: boolean
18
+
19
+ jobs:
20
+ test:
21
+ runs-on: blacksmith-4vcpu-ubuntu-2404
22
+
23
+ steps:
24
+ - name: Checkout code
25
+ uses: actions/checkout@v4
26
+
27
+ - name: Setup Python
28
+ uses: actions/setup-python@v5
29
+ with:
30
+ python-version: "3.13"
31
+ cache: "pip"
32
+
33
+ - name: Install dependencies
34
+ run: |
35
+ python -m pip install --upgrade pip
36
+ pip install -e ".[dev]"
37
+
38
+ - name: Run linter
39
+ run: ruff check .
40
+
41
+ - name: Run formatter check
42
+ run: ruff format --check .
43
+
44
+ - name: Run tests with coverage
45
+ run: pytest --cov=src --cov-report=xml --cov-report=term
46
+
47
+ - name: Upload coverage reports to Codecov
48
+ if: inputs.skip_codecov != true
49
+ uses: codecov/codecov-action@v5
50
+ with:
51
+ token: ${{ secrets.CODECOV_TOKEN }}
52
+ files: ./coverage.xml
53
+ fail_ci_if_error: true
54
+
55
+ integration-test:
56
+ runs-on: blacksmith-4vcpu-ubuntu-2404
57
+ needs: test # Only run if unit tests pass
58
+ # Skip for PRs from forks (they don't have access to secrets)
59
+ # This runs on: pushes to main, PRs from same repo
60
+ if: >
61
+ github.event_name == 'push' ||
62
+ github.event.pull_request.head.repo.full_name == github.repository
63
+
64
+ steps:
65
+ - name: Checkout code
66
+ uses: actions/checkout@v4
67
+
68
+ - name: Setup Python
69
+ uses: actions/setup-python@v5
70
+ with:
71
+ python-version: "3.13"
72
+ cache: "pip"
73
+
74
+ - name: Install dependencies
75
+ run: |
76
+ python -m pip install --upgrade pip
77
+ pip install -e ".[dev]"
78
+
79
+ - name: Run integration tests
80
+ env:
81
+ AMIGO_API_KEY: ${{ secrets.AMIGO_API_KEY }}
82
+ AMIGO_API_KEY_ID: ${{ secrets.AMIGO_API_KEY_ID }}
83
+ AMIGO_ORGANIZATION_ID: ${{ secrets.AMIGO_ORGANIZATION_ID }}
84
+ AMIGO_USER_ID: ${{ secrets.AMIGO_USER_ID }}
85
+ AMIGO_BASE_URL: ${{ secrets.AMIGO_BASE_URL }}
86
+ run: pytest -m integration -v -s
@@ -0,0 +1,18 @@
1
+ .venv
2
+ .env
3
+ .pytest_cache
4
+ .ruff_cache
5
+ __pycache__
6
+
7
+ .vscode
8
+ !.vscode/settings.json
9
+
10
+ dist/
11
+
12
+ # Coverage files
13
+ htmlcov/
14
+ coverage.xml
15
+ .coverage
16
+
17
+ # CI/Temp OpenAPI spec (fetched during workflows)
18
+ specs/openapi-new.json
@@ -0,0 +1,137 @@
1
+ # Contributing Guide
2
+
3
+ ## Quick Setup
4
+
5
+ ```bash
6
+ # Create and activate virtual environment
7
+ python -m venv .venv
8
+ source .venv/bin/activate
9
+
10
+ # Install the project in development mode
11
+ pip install -e ".[dev]"
12
+ ```
13
+
14
+ ## Development Commands
15
+
16
+ ```bash
17
+ check # Run all checks (format, lint, tests)
18
+ check --fix # Auto-fix issues and run all checks
19
+ check --fast # Format + lint only (skip tests)
20
+
21
+ gen-models # Generate models from API spec
22
+ ```
23
+
24
+ ## Workflow
25
+
26
+ 1. **Before committing:** Run `check --fix` to auto-fix issues
27
+ 2. **During development:** Use `check --fast` for quick validation
28
+ 3. **Update models:** Run `gen-models` when API changes
29
+
30
+ ## Release Process
31
+
32
+ ### GitHub Actions Release
33
+
34
+ 1. Go to the **Actions** tab in GitHub
35
+ 2. Select the **Release** workflow
36
+ 3. Click **Run workflow** and choose:
37
+ - **Version type**: `patch` (default), `minor`, or `major`
38
+ - **Dry run**: Test the release process without publishing
39
+
40
+ The workflow will automatically:
41
+
42
+ - ✅ Run all tests, linting, and formatting checks (reuses existing test workflow)
43
+ - 🔄 Generate fresh models from the API spec
44
+ - 📈 Increment the version using Hatch
45
+ - 📦 Build the package
46
+ - 🚀 Upload to PyPI
47
+ - 🏷️ Create a Git tag and GitHub release
48
+ - 📋 Provide detailed summary with links
49
+
50
+ ### Required Repository Secrets
51
+
52
+ Configure these secrets in your GitHub repository settings:
53
+
54
+ - `PYPI_API_TOKEN`: Token for https://pypi.org/ (production releases)
55
+ - `CODECOV_TOKEN`: Token for Codecov uploads (used by CI test workflow)
56
+ - `RELEASE_BOT_APP_ID`: GitHub App ID used to create tags/releases
57
+ - `RELEASE_BOT_PRIVATE_KEY`: GitHub App private key (PEM) for the release bot
58
+
59
+ ### Getting API Tokens
60
+
61
+ 1. **PyPI**: Go to https://pypi.org/manage/account/token/
62
+ 2. Create a token with upload permissions
63
+ 3. Add the token to your repository secrets
64
+
65
+ ## Auto-Release (OpenAPI changes)
66
+
67
+ When the backend OpenAPI spec changes, the SDK can auto-release a new version.
68
+
69
+ - Trigger: a `repository_dispatch` event with type `openapi-updated`
70
+ - Detect job: fetches the provided `spec_url` (or defaults to `https://api.amigo.ai/v1/openapi.json`), normalizes and compares against `specs/openapi-baseline.json`
71
+ - If changed: invokes the release workflow with `version_type=minor` and passes `spec_url` (supports `dry_run`)
72
+ - Release workflow: regenerates models, bumps version, builds, publishes to PyPI, pushes tag, and creates a GitHub release
73
+
74
+ Manual trigger example (repository_dispatch):
75
+
76
+ ```bash
77
+ curl -X POST \
78
+ -H "Accept: application/vnd.github+json" \
79
+ -H "Authorization: Bearer $GH_TOKEN" \
80
+ https://api.github.com/repos/<OWNER>/<REPO>/dispatches \
81
+ -d '{
82
+ "event_type": "openapi-updated",
83
+ "client_payload": {
84
+ "spec_url": "https://api.amigo.ai/v1/openapi.json",
85
+ "dry_run": true
86
+ }
87
+ }'
88
+ ```
89
+
90
+ Notes:
91
+
92
+ - The baseline file `specs/openapi-baseline.json` is created on first run and updated as part of the release workflow.
93
+ - Ensure the secrets listed above are configured so the workflow can push tags, create releases, and publish to PyPI.
94
+
95
+ ### Manual trigger using GitHub CLI (gh)
96
+
97
+ Requires `gh` authenticated with a token that can dispatch events on the SDK repos.
98
+
99
+ ```bash
100
+ # Values that mirror the workflow
101
+ OWNER="amigo-ai"
102
+ BACKEND_REPO="$OWNER/backend"
103
+ COMMIT_SHA="$(git rev-parse HEAD 2>/dev/null || echo manual-test)"
104
+ RUN_ID="$(date +%s)" # stand-in for GitHub Actions run_id
105
+
106
+ for repo in amigo-typescript-sdk amigo-python-sdk; do
107
+ echo "Notifying $repo..."
108
+ gh api "repos/$OWNER/$repo/dispatches" \
109
+ --method POST \
110
+ --header 'Accept: application/vnd.github+json' \
111
+ --input - <<EOF || echo "⚠️ Failed to notify $repo"
112
+ {
113
+ "event_type": "openapi-updated",
114
+ "client_payload": {
115
+ "spec_url": "https://api.amigo.ai/v1/openapi.json",
116
+ "commit_sha": "$COMMIT_SHA",
117
+ "backend_repo": "$BACKEND_REPO",
118
+ "backend_run_id": "$RUN_ID"
119
+ }
120
+ }
121
+ EOF
122
+ done
123
+ ```
124
+
125
+ ## IDE Setup (VS Code)
126
+
127
+ Install extensions:
128
+
129
+ - [Ruff](https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff)
130
+ - [Python](https://marketplace.visualstudio.com/items?itemName=ms-python.python)
131
+
132
+ ## Troubleshooting
133
+
134
+ - **Command not found:** Activate virtual environment with `source .venv/bin/activate`
135
+ - **Linting failures:** Run `check --fix` to auto-fix issues
136
+ - **Model import errors:** Run `gen-models` to regenerate models
137
+ - **Release failures:** Check API tokens are configured in repository secrets and try a dry run first
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Amigo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.