imbi-assistant 1.0.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 (39) hide show
  1. imbi_assistant-1.0.0/.github/workflows/docker.yml +85 -0
  2. imbi_assistant-1.0.0/.github/workflows/publish.yml +45 -0
  3. imbi_assistant-1.0.0/.github/workflows/test.yml +46 -0
  4. imbi_assistant-1.0.0/.gitignore +8 -0
  5. imbi_assistant-1.0.0/.pre-commit-config.yaml +22 -0
  6. imbi_assistant-1.0.0/.python-version +1 -0
  7. imbi_assistant-1.0.0/Dockerfile +24 -0
  8. imbi_assistant-1.0.0/LICENSE +21 -0
  9. imbi_assistant-1.0.0/PKG-INFO +18 -0
  10. imbi_assistant-1.0.0/README.md +2 -0
  11. imbi_assistant-1.0.0/justfile +51 -0
  12. imbi_assistant-1.0.0/pyproject.toml +174 -0
  13. imbi_assistant-1.0.0/src/imbi_assistant/__init__.py +18 -0
  14. imbi_assistant-1.0.0/src/imbi_assistant/age_ops.py +341 -0
  15. imbi_assistant-1.0.0/src/imbi_assistant/app.py +62 -0
  16. imbi_assistant-1.0.0/src/imbi_assistant/app_status.py +41 -0
  17. imbi_assistant-1.0.0/src/imbi_assistant/auth.py +148 -0
  18. imbi_assistant-1.0.0/src/imbi_assistant/client.py +77 -0
  19. imbi_assistant-1.0.0/src/imbi_assistant/client_tools.py +94 -0
  20. imbi_assistant-1.0.0/src/imbi_assistant/endpoints.py +582 -0
  21. imbi_assistant-1.0.0/src/imbi_assistant/mcp.py +235 -0
  22. imbi_assistant-1.0.0/src/imbi_assistant/models.py +131 -0
  23. imbi_assistant-1.0.0/src/imbi_assistant/py.typed +0 -0
  24. imbi_assistant-1.0.0/src/imbi_assistant/settings.py +92 -0
  25. imbi_assistant-1.0.0/src/imbi_assistant/system_prompt.md +73 -0
  26. imbi_assistant-1.0.0/src/imbi_assistant/system_prompt.py +83 -0
  27. imbi_assistant-1.0.0/tests/__init__.py +0 -0
  28. imbi_assistant-1.0.0/tests/helpers.py +40 -0
  29. imbi_assistant-1.0.0/tests/test_age_ops.py +327 -0
  30. imbi_assistant-1.0.0/tests/test_app.py +61 -0
  31. imbi_assistant-1.0.0/tests/test_auth.py +274 -0
  32. imbi_assistant-1.0.0/tests/test_client.py +139 -0
  33. imbi_assistant-1.0.0/tests/test_endpoints.py +1029 -0
  34. imbi_assistant-1.0.0/tests/test_mcp.py +316 -0
  35. imbi_assistant-1.0.0/tests/test_models.py +210 -0
  36. imbi_assistant-1.0.0/tests/test_settings.py +148 -0
  37. imbi_assistant-1.0.0/tests/test_system_prompt.py +125 -0
  38. imbi_assistant-1.0.0/tests/test_version.py +50 -0
  39. imbi_assistant-1.0.0/uv.lock +2387 -0
@@ -0,0 +1,85 @@
1
+ name: "Build and Publish Docker Image"
2
+ on:
3
+ release:
4
+ types: [published]
5
+
6
+ jobs:
7
+ build-python-package:
8
+ runs-on: ubuntu-latest
9
+ permissions:
10
+ contents: write
11
+ steps:
12
+ - uses: actions/checkout@v5
13
+
14
+ - name: Install uv
15
+ uses: astral-sh/setup-uv@v7
16
+ with:
17
+ enable-cache: true
18
+
19
+ - name: Build python package
20
+ run: uv build
21
+
22
+ - name: Upload build artifacts
23
+ uses: actions/upload-artifact@v6
24
+ with:
25
+ name: python-distributions
26
+ path: dist/*
27
+
28
+ - name: Attach distributions
29
+ env:
30
+ GH_TOKEN: ${{ github.token }}
31
+ run: gh release upload ${{ github.event.release.tag_name }} dist/*.whl dist/*.tar.gz --clobber
32
+
33
+ build-and-publish-docker:
34
+ runs-on: ubuntu-latest
35
+ needs: build-python-package
36
+ permissions:
37
+ attestations: write
38
+ contents: read
39
+ id-token: write
40
+ packages: write
41
+ steps:
42
+ - uses: actions/checkout@v5
43
+
44
+ - name: Download python distributions
45
+ uses: actions/download-artifact@v6
46
+ with:
47
+ name: python-distributions
48
+ path: dist
49
+
50
+ - uses: docker/setup-qemu-action@v3
51
+
52
+ - uses: docker/setup-buildx-action@v3
53
+
54
+ - name: Log in to ghcr.io
55
+ uses: docker/login-action@v3
56
+ with:
57
+ registry: ghcr.io
58
+ username: ${{ github.actor }}
59
+ password: ${{ secrets.GITHUB_TOKEN }}
60
+
61
+ - id: meta
62
+ uses: docker/metadata-action@v5
63
+ with:
64
+ images: ghcr.io/${{ github.repository }}
65
+ tags: |
66
+ type=semver,pattern={{version}}
67
+ type=semver,pattern={{major}}.{{minor}}
68
+ type=semver,pattern={{major}}
69
+
70
+ - id: build
71
+ uses: docker/build-push-action@v6
72
+ with:
73
+ context: .
74
+ platforms: linux/amd64,linux/arm64
75
+ push: true
76
+ tags: ${{ steps.meta.outputs.tags }}
77
+ labels: ${{ steps.meta.outputs.labels }}
78
+ cache-from: type=gha
79
+ cache-to: type=gha,mode=max
80
+
81
+ - uses: actions/attest-build-provenance@v3
82
+ with:
83
+ subject-name: ghcr.io/${{ github.repository }}
84
+ subject-digest: ${{ steps.build.outputs.digest }}
85
+ push-to-registry: true
@@ -0,0 +1,45 @@
1
+ name: Publish to PyPI
2
+ on:
3
+ release:
4
+ types: [published]
5
+
6
+ permissions:
7
+ id-token: write
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - name: Checkout repository
14
+ uses: actions/checkout@v6
15
+ with:
16
+ fetch-depth: 0
17
+
18
+ - name: Install uv
19
+ uses: astral-sh/setup-uv@v5
20
+
21
+ - name: Set up Python
22
+ run: uv python install 3.14
23
+
24
+ - name: Build
25
+ run: uv build
26
+
27
+ - name: Upload dist
28
+ uses: actions/upload-artifact@v4
29
+ with:
30
+ name: dist
31
+ path: dist/
32
+
33
+ publish:
34
+ needs: build
35
+ runs-on: ubuntu-latest
36
+ environment: pypi
37
+ steps:
38
+ - name: Download dist
39
+ uses: actions/download-artifact@v4
40
+ with:
41
+ name: dist
42
+ path: dist/
43
+
44
+ - name: Publish package
45
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,46 @@
1
+ name: "Run Tests"
2
+ on:
3
+ push:
4
+ branches: [ main ]
5
+ pull_request:
6
+ branches: [ main ]
7
+
8
+ jobs:
9
+ lint:
10
+ name: "Static Analysis"
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - name: Checkout repository
14
+ uses: actions/checkout@v5
15
+ - name: Install just
16
+ uses: extractions/setup-just@v2
17
+ - name: Install uv
18
+ uses: astral-sh/setup-uv@v7
19
+ with:
20
+ enable-cache: true
21
+ - name: Run lint checks
22
+ run: just lint
23
+
24
+ test:
25
+ name: "Automated Tests"
26
+ runs-on: ubuntu-latest
27
+ steps:
28
+ - name: Checkout repository
29
+ uses: actions/checkout@v5
30
+ - name: Install just
31
+ uses: extractions/setup-just@v2
32
+ - name: Install uv
33
+ uses: astral-sh/setup-uv@v7
34
+ with:
35
+ enable-cache: true
36
+ python-version: "3.14"
37
+ - name: Run tests
38
+ env:
39
+ UV_FROZEN: "1"
40
+ run: just test
41
+ - name: Upload coverage reports
42
+ uses: codecov/codecov-action@v5
43
+ with:
44
+ token: ${{ secrets.CODECOV_TOKEN }}
45
+ files: "build/coverage.xml"
46
+ flags: unittests
@@ -0,0 +1,8 @@
1
+ .*
2
+ !/.coderabbit.yaml
3
+ !/.github
4
+ !/.gitignore
5
+ !/.pre-commit-config.yaml
6
+ !/.python-version
7
+ /build
8
+ /dist
@@ -0,0 +1,22 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v6.0.0 # unrelated to the pre-commit utility version
4
+ hooks:
5
+ - id: check-executables-have-shebangs
6
+ - id: check-json
7
+ - id: check-shebang-scripts-are-executable
8
+ - id: check-toml
9
+ - id: check-yaml
10
+ - id: end-of-file-fixer
11
+ - id: mixed-line-ending
12
+ - id: trailing-whitespace
13
+ - repo: https://github.com/astral-sh/ruff-pre-commit
14
+ rev: v0.15.0
15
+ hooks:
16
+ - id: ruff-check
17
+ args: [--fix]
18
+ - id: ruff-format
19
+ - repo: https://github.com/tombi-toml/tombi-pre-commit
20
+ rev: v0.7.27
21
+ hooks:
22
+ - id: tombi-format
@@ -0,0 +1 @@
1
+ 3.14
@@ -0,0 +1,24 @@
1
+ FROM python:3.14-slim AS builder
2
+
3
+ WORKDIR /tmp/install
4
+
5
+ RUN pip install uv \
6
+ && apt update \
7
+ && apt install git --yes
8
+
9
+ COPY dist/*.whl pyproject.toml uv.lock /tmp/install/
10
+
11
+ RUN uv venv /app \
12
+ && . /app/bin/activate \
13
+ && uv sync --no-dev --active --frozen --no-install-project \
14
+ && uv pip install *.whl
15
+
16
+ FROM python:3.14-slim AS service
17
+ EXPOSE 8002
18
+ ENV PATH="/app/bin:$PATH"
19
+ WORKDIR /app
20
+ COPY --from=builder /app/ /app/
21
+ RUN useradd -r -g users imbi \
22
+ && chown -R imbi /app
23
+ USER imbi
24
+ CMD ["/app/bin/imbi-assistant", "serve", "--host", "0.0.0.0", "--port", "8002"]
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Imbi
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.
@@ -0,0 +1,18 @@
1
+ Metadata-Version: 2.4
2
+ Name: imbi-assistant
3
+ Version: 1.0.0
4
+ Summary: Imbi AI assistant service
5
+ Author-email: "Gavin M. Roy" <gavinr@aweber.com>
6
+ License-Expression: BSD-3-Clause
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.14
9
+ Requires-Dist: anthropic>=0.84.0
10
+ Requires-Dist: fastapi>=0.135.1
11
+ Requires-Dist: fastmcp>=2.0.0
12
+ Requires-Dist: httpx>=0.28.1
13
+ Requires-Dist: imbi-common[databases,server]>=0.3.0
14
+ Requires-Dist: pydantic~=2.12
15
+ Description-Content-Type: text/markdown
16
+
17
+ # imbi-assistant
18
+ HTTP backend for Imbi's AI Assistant
@@ -0,0 +1,2 @@
1
+ # imbi-assistant
2
+ HTTP backend for Imbi's AI Assistant
@@ -0,0 +1,51 @@
1
+ [doc("Bootstrap the environment and run the service in the foreground")]
2
+ [group("Testing")]
3
+ serve *ARGS: setup
4
+ uv run --env-file=.env imbi-assistant serve {{ ARGS }}
5
+
6
+ [default]
7
+ [private]
8
+ ci: lint test
9
+
10
+ [doc("Set up your development environment")]
11
+ [group("Environment")]
12
+ setup:
13
+ uv sync --all-groups --all-extras --frozen
14
+ uv run pre-commit install --install-hooks --overwrite
15
+
16
+ [doc("Run tests")]
17
+ [group("Testing")]
18
+ test: setup
19
+ uv run pytest
20
+
21
+ [doc("Run linters")]
22
+ [group("Testing")]
23
+ lint: setup
24
+ uv run pre-commit run --all-files
25
+ uv run basedpyright
26
+ uv run mypy
27
+
28
+ [doc("Reformat code (optionally pass specific files)")]
29
+ [group("Development")]
30
+ format *FILES: setup
31
+ #!/usr/bin/env sh
32
+ set -ex
33
+ if [ "{{FILES}}" = '' ]; then
34
+ args='--all-files'
35
+ else
36
+ args='--files {{FILES}}'
37
+ fi
38
+ uv run pre-commit run ruff-format $args
39
+ uv run pre-commit run tombi-format $args
40
+
41
+ [doc("Remove runtime artifacts")]
42
+ [group("Environment")]
43
+ clean:
44
+ rm -f .coverage
45
+ rm -fR build
46
+
47
+ [confirm]
48
+ [doc("Remove caches, virtual env, and output files")]
49
+ [group("Environment")]
50
+ real-clean: clean
51
+ rm -fR .venv .*_cache dist
@@ -0,0 +1,174 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "imbi-assistant"
7
+ version = "1.0.0"
8
+ description = "Imbi AI assistant service"
9
+ readme = "README.md"
10
+ requires-python = ">=3.14"
11
+ license = "BSD-3-Clause"
12
+ authors = [{ name = "Gavin M. Roy", email = "gavinr@aweber.com" }]
13
+ dependencies = [
14
+ "anthropic>=0.84.0",
15
+ "fastapi>=0.135.1",
16
+ "fastmcp>=2.0.0",
17
+ "httpx>=0.28.1",
18
+ "imbi-common[databases,server]>=0.3.0",
19
+ "pydantic~=2.12",
20
+ ]
21
+
22
+ [project.scripts]
23
+ imbi-assistant = "imbi_assistant.app:cli"
24
+
25
+ [dependency-groups]
26
+ dev = [
27
+ "basedpyright>=1.37.1",
28
+ "coverage[toml]>=7.13.1",
29
+ "httpx>=0.28.1",
30
+ "mypy~=1.19.1",
31
+ "pre-commit>=4.5.1",
32
+ "pytest>=9.0.2",
33
+ "pytest-cov>=7.0.0",
34
+ "python-dotenv>=1.1.0",
35
+ "ruff~=0.14.6",
36
+ ]
37
+
38
+ [tool.coverage.report]
39
+ fail_under = 90
40
+ show_missing = true
41
+
42
+ [tool.coverage.run]
43
+ branch = true
44
+ source = ["src"]
45
+
46
+ [tool.mypy]
47
+ namespace_packages = true
48
+ show_error_codes = true
49
+ strict = true
50
+ packages = ["imbi_assistant", "tests"]
51
+
52
+ [[tool.mypy.overrides]]
53
+ module = "tests.*"
54
+ disallow_untyped_defs = false
55
+ disallow_untyped_calls = false
56
+ disallow_any_expr = false
57
+ warn_return_any = false
58
+ disable_error_code = [
59
+ "call-arg",
60
+ "arg-type",
61
+ "type-arg",
62
+ "union-attr",
63
+ "index",
64
+ "var-annotated"
65
+ ]
66
+
67
+ [tool.pyright]
68
+ deprecateTypingAliases = true
69
+ reportMissingSuperCall = "hint"
70
+ typeCheckingMode = "strict"
71
+
72
+ [[tool.pyright.executionEnvironments]]
73
+ root = "src"
74
+ reportUnusedFunction = "warning"
75
+
76
+ [[tool.pyright.executionEnvironments]]
77
+ root = "tests"
78
+ reportUnusedFunction = "none"
79
+ reportPrivateUsage = "none"
80
+ reportUnknownLambdaType = "none"
81
+ reportUnknownParameterType = "none"
82
+ reportUnknownVariableType = "none"
83
+ reportMissingTypeArgument = "none"
84
+ reportCallIssue = "none"
85
+ reportOptionalMemberAccess = "none"
86
+ reportOptionalSubscript = "none"
87
+ reportUnknownMemberType = "none"
88
+ reportArgumentType = "none"
89
+ reportUnknownArgumentType = "none"
90
+ extraPaths = ["src", "."]
91
+
92
+ [tool.ruff]
93
+ line-length = 79
94
+ target-version = "py314"
95
+
96
+ [tool.ruff.format]
97
+ quote-style = "single"
98
+
99
+ [tool.ruff.lint]
100
+ select = [
101
+ "A",
102
+ "ANN",
103
+ "ARG",
104
+ "ASYNC",
105
+ "B",
106
+ "BLE",
107
+ "C4",
108
+ "C90",
109
+ "DTZ",
110
+ "E",
111
+ "ERA",
112
+ "F",
113
+ "FLY",
114
+ "FURB",
115
+ "G",
116
+ "I",
117
+ "LOG",
118
+ "N",
119
+ "PIE",
120
+ "PL",
121
+ "PTH",
122
+ "Q",
123
+ "RET",
124
+ "RSE",
125
+ "RUF",
126
+ "S",
127
+ "SIM",
128
+ "T10",
129
+ "T20",
130
+ "TC",
131
+ "TRY",
132
+ "W",
133
+ ]
134
+ ignore = [
135
+ "ANN401",
136
+ "ARG002",
137
+ "PLR0913",
138
+ "PLR2004",
139
+ "TRY003",
140
+ "TRY400",
141
+ ]
142
+ flake8-quotes = { inline-quotes = "single" }
143
+
144
+ [tool.ruff.lint.flake8-type-checking]
145
+ runtime-evaluated-base-classes = ["pydantic.BaseModel"]
146
+ runtime-evaluated-decorators = [
147
+ "fastapi.APIRouter.get",
148
+ "fastapi.APIRouter.post",
149
+ "fastapi.APIRouter.put",
150
+ "fastapi.APIRouter.patch",
151
+ "fastapi.APIRouter.delete",
152
+ ]
153
+
154
+ [tool.ruff.lint.mccabe]
155
+ max-complexity = 15
156
+
157
+ [tool.ruff.lint.per-file-ignores]
158
+ "src/imbi_assistant/client.py" = ["PLW0603"]
159
+ "src/imbi_assistant/mcp.py" = ["PLW0603"]
160
+ "src/imbi_assistant/settings.py" = ["PLW0603"]
161
+ "src/imbi_assistant/system_prompt.py" = ["PLW0603"]
162
+ "tests/**/*.py" = ["ANN", "ARG005", "B018", "PLC0415", "S"]
163
+
164
+ [tool.pytest.ini_options]
165
+ testpaths = ["tests"]
166
+ addopts = [
167
+ "--cov=src/imbi_assistant",
168
+ "--cov-report=xml:build/coverage.xml",
169
+ "--cov-report=term",
170
+ ]
171
+
172
+ [[tool.uv.index]]
173
+ url = "https://pypi.org/simple"
174
+ default = true
@@ -0,0 +1,18 @@
1
+ import re as _re
2
+ from importlib import metadata as _metadata
3
+
4
+ try:
5
+ version = _metadata.version('imbi-assistant')
6
+ except _metadata.PackageNotFoundError:
7
+ version = '0.0.0'
8
+
9
+ version_info: list[int | str] = []
10
+ for _part in version.split('.'):
11
+ _match = _re.fullmatch(r'(\d+)(.*)', _part)
12
+ if _match is None:
13
+ version_info.append(_part)
14
+ else:
15
+ version_info.append(int(_match.group(1)))
16
+ if _match.group(2):
17
+ version_info.append(_match.group(2))
18
+ del _metadata, _re