lexsi-sdk 0.1.12__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.
- lexsi_sdk-0.1.12/.github/scripts/release.py +46 -0
- lexsi_sdk-0.1.12/.github/scripts/test-release.py +52 -0
- lexsi_sdk-0.1.12/.github/workflows/publish.yml +83 -0
- lexsi_sdk-0.1.12/.github/workflows/release.yml +28 -0
- lexsi_sdk-0.1.12/.github/workflows/test-publish.yml +93 -0
- lexsi_sdk-0.1.12/.github/workflows/test-release.yml +20 -0
- lexsi_sdk-0.1.12/.gitignore +29 -0
- lexsi_sdk-0.1.12/LICENSE +21 -0
- lexsi_sdk-0.1.12/MANIFEST.in +8 -0
- lexsi_sdk-0.1.12/PKG-INFO +95 -0
- lexsi_sdk-0.1.12/lexsi_sdk/__init__.py +5 -0
- lexsi_sdk-0.1.12/lexsi_sdk/client/__init__.py +0 -0
- lexsi_sdk-0.1.12/lexsi_sdk/client/client.py +194 -0
- lexsi_sdk-0.1.12/lexsi_sdk/common/__init__.py +0 -0
- lexsi_sdk-0.1.12/lexsi_sdk/common/config/.env.prod +3 -0
- lexsi_sdk-0.1.12/lexsi_sdk/common/constants.py +143 -0
- lexsi_sdk-0.1.12/lexsi_sdk/common/enums.py +8 -0
- lexsi_sdk-0.1.12/lexsi_sdk/common/environment.py +47 -0
- lexsi_sdk-0.1.12/lexsi_sdk/common/monitoring.py +81 -0
- lexsi_sdk-0.1.12/lexsi_sdk/common/trigger.py +75 -0
- lexsi_sdk-0.1.12/lexsi_sdk/common/types.py +122 -0
- lexsi_sdk-0.1.12/lexsi_sdk/common/utils.py +84 -0
- lexsi_sdk-0.1.12/lexsi_sdk/common/validation.py +83 -0
- lexsi_sdk-0.1.12/lexsi_sdk/common/xai_uris.py +197 -0
- lexsi_sdk-0.1.12/lexsi_sdk/core/__init__.py +0 -0
- lexsi_sdk-0.1.12/lexsi_sdk/core/agent.py +62 -0
- lexsi_sdk-0.1.12/lexsi_sdk/core/alert.py +56 -0
- lexsi_sdk-0.1.12/lexsi_sdk/core/case.py +618 -0
- lexsi_sdk-0.1.12/lexsi_sdk/core/dashboard.py +131 -0
- lexsi_sdk-0.1.12/lexsi_sdk/core/guardrails/__init__.py +0 -0
- lexsi_sdk-0.1.12/lexsi_sdk/core/guardrails/guard_template.py +299 -0
- lexsi_sdk-0.1.12/lexsi_sdk/core/guardrails/guardrail_autogen.py +554 -0
- lexsi_sdk-0.1.12/lexsi_sdk/core/guardrails/guardrails_langgraph.py +525 -0
- lexsi_sdk-0.1.12/lexsi_sdk/core/guardrails/guardrails_openai.py +541 -0
- lexsi_sdk-0.1.12/lexsi_sdk/core/guardrails/openai_runner.py +1328 -0
- lexsi_sdk-0.1.12/lexsi_sdk/core/model_summary.py +110 -0
- lexsi_sdk-0.1.12/lexsi_sdk/core/organization.py +549 -0
- lexsi_sdk-0.1.12/lexsi_sdk/core/project.py +5131 -0
- lexsi_sdk-0.1.12/lexsi_sdk/core/synthetic.py +387 -0
- lexsi_sdk-0.1.12/lexsi_sdk/core/text.py +640 -0
- lexsi_sdk-0.1.12/lexsi_sdk/core/tracer.py +208 -0
- lexsi_sdk-0.1.12/lexsi_sdk/core/utils.py +36 -0
- lexsi_sdk-0.1.12/lexsi_sdk/core/workspace.py +325 -0
- lexsi_sdk-0.1.12/lexsi_sdk/core/wrapper.py +766 -0
- lexsi_sdk-0.1.12/lexsi_sdk/core/xai.py +306 -0
- lexsi_sdk-0.1.12/lexsi_sdk/version.py +34 -0
- lexsi_sdk-0.1.12/lexsi_sdk.egg-info/PKG-INFO +95 -0
- lexsi_sdk-0.1.12/lexsi_sdk.egg-info/SOURCES.txt +90 -0
- lexsi_sdk-0.1.12/lexsi_sdk.egg-info/dependency_links.txt +1 -0
- lexsi_sdk-0.1.12/lexsi_sdk.egg-info/requires.txt +28 -0
- lexsi_sdk-0.1.12/lexsi_sdk.egg-info/top_level.txt +1 -0
- lexsi_sdk-0.1.12/lexsiai/__init__.py +5 -0
- lexsi_sdk-0.1.12/lexsiai/client/__init__.py +0 -0
- lexsi_sdk-0.1.12/lexsiai/client/client.py +194 -0
- lexsi_sdk-0.1.12/lexsiai/common/__init__.py +0 -0
- lexsi_sdk-0.1.12/lexsiai/common/config/.env.prod +3 -0
- lexsi_sdk-0.1.12/lexsiai/common/constants.py +143 -0
- lexsi_sdk-0.1.12/lexsiai/common/enums.py +8 -0
- lexsi_sdk-0.1.12/lexsiai/common/environment.py +47 -0
- lexsi_sdk-0.1.12/lexsiai/common/monitoring.py +81 -0
- lexsi_sdk-0.1.12/lexsiai/common/trigger.py +75 -0
- lexsi_sdk-0.1.12/lexsiai/common/types.py +122 -0
- lexsi_sdk-0.1.12/lexsiai/common/utils.py +84 -0
- lexsi_sdk-0.1.12/lexsiai/common/validation.py +83 -0
- lexsi_sdk-0.1.12/lexsiai/common/xai_uris.py +197 -0
- lexsi_sdk-0.1.12/lexsiai/core/__init__.py +0 -0
- lexsi_sdk-0.1.12/lexsiai/core/agent.py +62 -0
- lexsi_sdk-0.1.12/lexsiai/core/alert.py +56 -0
- lexsi_sdk-0.1.12/lexsiai/core/case.py +618 -0
- lexsi_sdk-0.1.12/lexsiai/core/dashboard.py +131 -0
- lexsi_sdk-0.1.12/lexsiai/core/guardrails/__init__.py +0 -0
- lexsi_sdk-0.1.12/lexsiai/core/guardrails/guard_template.py +299 -0
- lexsi_sdk-0.1.12/lexsiai/core/guardrails/guardrail_autogen.py +554 -0
- lexsi_sdk-0.1.12/lexsiai/core/guardrails/guardrails_langgraph.py +525 -0
- lexsi_sdk-0.1.12/lexsiai/core/guardrails/guardrails_openai.py +541 -0
- lexsi_sdk-0.1.12/lexsiai/core/guardrails/openai_runner.py +1328 -0
- lexsi_sdk-0.1.12/lexsiai/core/model_summary.py +110 -0
- lexsi_sdk-0.1.12/lexsiai/core/organization.py +549 -0
- lexsi_sdk-0.1.12/lexsiai/core/project.py +5135 -0
- lexsi_sdk-0.1.12/lexsiai/core/synthetic.py +387 -0
- lexsi_sdk-0.1.12/lexsiai/core/text.py +640 -0
- lexsi_sdk-0.1.12/lexsiai/core/tracer.py +208 -0
- lexsi_sdk-0.1.12/lexsiai/core/utils.py +36 -0
- lexsi_sdk-0.1.12/lexsiai/core/workspace.py +325 -0
- lexsi_sdk-0.1.12/lexsiai/core/wrapper.py +766 -0
- lexsi_sdk-0.1.12/lexsiai/core/xai.py +306 -0
- lexsi_sdk-0.1.12/lexsiai/version.py +34 -0
- lexsi_sdk-0.1.12/pyproject.toml +44 -0
- lexsi_sdk-0.1.12/readme.md +49 -0
- lexsi_sdk-0.1.12/requirements.txt +28 -0
- lexsi_sdk-0.1.12/setup.cfg +4 -0
- lexsi_sdk-0.1.12/tests/test_sdk_import.py +4 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import subprocess
|
|
3
|
+
|
|
4
|
+
default_version = "0.1.0"
|
|
5
|
+
|
|
6
|
+
def get_last_version() -> str:
|
|
7
|
+
"""Return the version number of the last release."""
|
|
8
|
+
result = subprocess.run(
|
|
9
|
+
["gh", "release", "list", "--limit", "1", "--json", "tagName", "--jq", ".[0].tagName"],
|
|
10
|
+
check=False,
|
|
11
|
+
stdout=subprocess.PIPE,
|
|
12
|
+
stderr=subprocess.PIPE,
|
|
13
|
+
text=True,
|
|
14
|
+
)
|
|
15
|
+
tag = result.stdout.strip()
|
|
16
|
+
if not tag:
|
|
17
|
+
return default_version
|
|
18
|
+
return tag
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def bump_patch_number(version_number: str) -> str:
|
|
22
|
+
"""Return a copy of `version_number` with the patch number incremented."""
|
|
23
|
+
major, minor, patch = version_number.split(".")
|
|
24
|
+
return f"{major}.{minor}.{int(patch) + 1}"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def create_new_patch_release():
|
|
28
|
+
"""Create a new patch release on GitHub."""
|
|
29
|
+
try:
|
|
30
|
+
last_version_number = get_last_version()
|
|
31
|
+
except subprocess.CalledProcessError as err:
|
|
32
|
+
# The project doesn't have any releases yet.
|
|
33
|
+
new_version_number = default_version
|
|
34
|
+
print(err)
|
|
35
|
+
print(f'taking default version: {new_version_number}')
|
|
36
|
+
else:
|
|
37
|
+
new_version_number = bump_patch_number(last_version_number)
|
|
38
|
+
|
|
39
|
+
subprocess.run(
|
|
40
|
+
["gh", "release", "create", "--generate-notes", f"{new_version_number}"],
|
|
41
|
+
check=True,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
create_new_patch_release()
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import subprocess
|
|
3
|
+
|
|
4
|
+
default_version = "0.0.dev1"
|
|
5
|
+
|
|
6
|
+
def get_last_version() -> str:
|
|
7
|
+
"""Return the version number of the last release."""
|
|
8
|
+
tag_info = (
|
|
9
|
+
subprocess.run(
|
|
10
|
+
"gh release list | grep -E '^.+\\.dev\\d*' | head -n 1",
|
|
11
|
+
shell=True,
|
|
12
|
+
check=True,
|
|
13
|
+
stdout=subprocess.PIPE,
|
|
14
|
+
stderr=subprocess.PIPE
|
|
15
|
+
)
|
|
16
|
+
.stdout.decode("utf8")
|
|
17
|
+
.strip()
|
|
18
|
+
)
|
|
19
|
+
tag_fields = tag_info.split('\t')
|
|
20
|
+
tag = tag_fields[2] if len(tag_fields) > 2 else default_version
|
|
21
|
+
|
|
22
|
+
return tag
|
|
23
|
+
|
|
24
|
+
def bump_patch_number(version_number: str) -> str:
|
|
25
|
+
"""Return a copy of `version_number` with the patch number incremented."""
|
|
26
|
+
major, minor, patch = version_number.split(".")
|
|
27
|
+
|
|
28
|
+
new_patch_version = int(patch[3:]) + 1
|
|
29
|
+
|
|
30
|
+
return f"{major}.{minor}.dev{new_patch_version}"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def create_new_patch_release():
|
|
34
|
+
"""Create a new patch release on GitHub."""
|
|
35
|
+
try:
|
|
36
|
+
last_version_number = get_last_version()
|
|
37
|
+
except subprocess.CalledProcessError as err:
|
|
38
|
+
# The project doesn't have any releases yet.
|
|
39
|
+
new_version_number = default_version
|
|
40
|
+
print(err)
|
|
41
|
+
print(f'taking default version: {new_version_number}')
|
|
42
|
+
else:
|
|
43
|
+
new_version_number = bump_patch_number(last_version_number)
|
|
44
|
+
|
|
45
|
+
subprocess.run(
|
|
46
|
+
["gh", "release", "create", "--generate-notes", f"{new_version_number}"],
|
|
47
|
+
check=True,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
if __name__ == "__main__":
|
|
52
|
+
create_new_patch_release()
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [created]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
pypi:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
contents: write
|
|
12
|
+
env:
|
|
13
|
+
SETUPTOOLS_SCM_PRETEND_VERSION: ${{ github.event.release.tag_name }}
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout
|
|
16
|
+
uses: actions/checkout@v3
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0
|
|
19
|
+
ref: ${{ github.event.release.tag_name }}
|
|
20
|
+
|
|
21
|
+
- name: Clean build artefacts
|
|
22
|
+
run: rm -rf dist build ./*.egg-info
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies and run tests
|
|
25
|
+
run: |
|
|
26
|
+
python -m pip install --upgrade pip
|
|
27
|
+
pip install -r requirements.txt
|
|
28
|
+
pip install .
|
|
29
|
+
pip install pytest
|
|
30
|
+
pytest tests/
|
|
31
|
+
continue-on-error: false
|
|
32
|
+
|
|
33
|
+
- name: Build package
|
|
34
|
+
run: python3 -m pip install --upgrade build && python3 -m build
|
|
35
|
+
|
|
36
|
+
- name: Check version is non-dev
|
|
37
|
+
run: |
|
|
38
|
+
python -m pip install setuptools_scm
|
|
39
|
+
python - << 'PY'
|
|
40
|
+
from setuptools_scm import get_version
|
|
41
|
+
v = get_version()
|
|
42
|
+
print("Computed version:", v)
|
|
43
|
+
if ".dev" in v:
|
|
44
|
+
raise SystemExit(f"Refusing to publish dev version: {v}")
|
|
45
|
+
PY
|
|
46
|
+
|
|
47
|
+
- name: Publish package to PyPI
|
|
48
|
+
if: success()
|
|
49
|
+
run: |
|
|
50
|
+
python -m pip install --upgrade twine
|
|
51
|
+
python -m pip install -U packaging
|
|
52
|
+
python -m twine upload dist/*
|
|
53
|
+
env:
|
|
54
|
+
TWINE_USERNAME: __token__
|
|
55
|
+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
|
|
56
|
+
|
|
57
|
+
- name: Send release notes to Slack
|
|
58
|
+
id: slack
|
|
59
|
+
uses: slackapi/slack-github-action@v1.24.0
|
|
60
|
+
with:
|
|
61
|
+
channel-id: "lexsi-sdk-prod-release"
|
|
62
|
+
payload: |
|
|
63
|
+
{
|
|
64
|
+
"text": "Lexsi SDK release",
|
|
65
|
+
"blocks": [
|
|
66
|
+
{
|
|
67
|
+
"type": "section",
|
|
68
|
+
"text": {
|
|
69
|
+
"type": "mrkdwn",
|
|
70
|
+
"text": "*Version:* ${{ github.event.release.tag_name }}"
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"type": "section",
|
|
75
|
+
"text": {
|
|
76
|
+
"type": "mrkdwn",
|
|
77
|
+
"text": "*PyPI URL:* https://pypi.org/project/lexsi_sdk/${{ github.event.release.tag_name }}"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
env:
|
|
83
|
+
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: Create release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main # when you push to main, this runs
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
github:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: write # needed so gh can create tags/releases
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout
|
|
16
|
+
uses: actions/checkout@v3
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0 # full history so setuptools_scm & gh see all tags
|
|
19
|
+
|
|
20
|
+
- name: Set up Python
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: '3.11'
|
|
24
|
+
|
|
25
|
+
- name: Create new patch release
|
|
26
|
+
run: .github/scripts/release.py
|
|
27
|
+
env:
|
|
28
|
+
GH_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
name: Publish to test PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
branch:
|
|
7
|
+
description: "Branch to release from"
|
|
8
|
+
required: true
|
|
9
|
+
default: "main"
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
pypi:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout
|
|
17
|
+
uses: actions/checkout@v4
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0
|
|
20
|
+
ref: ${{ github.event.inputs.branch }}
|
|
21
|
+
|
|
22
|
+
- name: Fetch all tags
|
|
23
|
+
run: git fetch --tags --force
|
|
24
|
+
|
|
25
|
+
- name: Detect latest stable version
|
|
26
|
+
run: |
|
|
27
|
+
latest_tag=$(git tag --list --sort=-v:refname | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1)
|
|
28
|
+
|
|
29
|
+
if [ -z "$latest_tag" ]; then
|
|
30
|
+
echo "No stable tags found. Using fallback 0.1.0"
|
|
31
|
+
base="0.1.0"
|
|
32
|
+
else
|
|
33
|
+
echo "Latest stable tag detected: $latest_tag"
|
|
34
|
+
base="$latest_tag"
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
echo "BASE_VERSION=$base" >> $GITHUB_ENV
|
|
38
|
+
echo "Using base version: $base"
|
|
39
|
+
|
|
40
|
+
- name: Upgrade packaging stack
|
|
41
|
+
run: |
|
|
42
|
+
python -m pip install --upgrade pip
|
|
43
|
+
pip install --upgrade setuptools wheel setuptools_scm build twine pkginfo readme-renderer requests
|
|
44
|
+
|
|
45
|
+
- name: Compute next dev version
|
|
46
|
+
run: |
|
|
47
|
+
python <<'EOF'
|
|
48
|
+
import os, re, requests
|
|
49
|
+
|
|
50
|
+
base = os.environ["BASE_VERSION"]
|
|
51
|
+
pkg = "lexsi-sdk"
|
|
52
|
+
|
|
53
|
+
url = f"https://test.pypi.org/pypi/{pkg}/json"
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
data = requests.get(url, timeout=10).json()
|
|
57
|
+
versions = data.get("releases", {}).keys()
|
|
58
|
+
except Exception:
|
|
59
|
+
print("No existing TestPyPI project found. Starting dev counter at 1.")
|
|
60
|
+
versions = []
|
|
61
|
+
|
|
62
|
+
pattern = re.compile(rf"^{re.escape(base)}\.dev(\d+)$")
|
|
63
|
+
|
|
64
|
+
dev_nums = [
|
|
65
|
+
int(m.group(1))
|
|
66
|
+
for v in versions
|
|
67
|
+
if (m := pattern.match(v))
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
next_dev = max(dev_nums, default=0) + 1
|
|
71
|
+
final_version = f"{base}.dev{next_dev}"
|
|
72
|
+
|
|
73
|
+
print("Next dev version:", final_version)
|
|
74
|
+
|
|
75
|
+
with open(os.environ["GITHUB_ENV"], "a") as f:
|
|
76
|
+
f.write(f"SETUPTOOLS_SCM_PRETEND_VERSION={final_version}\n")
|
|
77
|
+
|
|
78
|
+
EOF
|
|
79
|
+
|
|
80
|
+
- name: Build package
|
|
81
|
+
env:
|
|
82
|
+
XAI_ENV: testing
|
|
83
|
+
run: |
|
|
84
|
+
rm -rf build dist *.egg-info
|
|
85
|
+
python -m build
|
|
86
|
+
twine check dist/*
|
|
87
|
+
|
|
88
|
+
- name: Publish package to Test PyPI
|
|
89
|
+
env:
|
|
90
|
+
TWINE_USERNAME: __token__
|
|
91
|
+
TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }}
|
|
92
|
+
run: |
|
|
93
|
+
python -m twine upload --repository-url https://test.pypi.org/legacy/ dist/*
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: Create test release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- testing-main
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
github:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
contents: write
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout
|
|
16
|
+
uses: actions/checkout@v3
|
|
17
|
+
- name: Create new test release
|
|
18
|
+
run: .github/scripts/test-release.py
|
|
19
|
+
env:
|
|
20
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
.vscode
|
|
2
|
+
|
|
3
|
+
# Compiled python modules.
|
|
4
|
+
*.pyc
|
|
5
|
+
__pycache__
|
|
6
|
+
|
|
7
|
+
# Setuptools distribution folder.
|
|
8
|
+
/dist/
|
|
9
|
+
build
|
|
10
|
+
|
|
11
|
+
# Python egg metadata, regenerated from source files by setuptools.
|
|
12
|
+
*.egg-info
|
|
13
|
+
.eggs
|
|
14
|
+
|
|
15
|
+
# Documentation files
|
|
16
|
+
_build
|
|
17
|
+
_static
|
|
18
|
+
_templates
|
|
19
|
+
|
|
20
|
+
# Venv
|
|
21
|
+
*venv
|
|
22
|
+
|
|
23
|
+
# env file
|
|
24
|
+
.env.local
|
|
25
|
+
.env.testing
|
|
26
|
+
|
|
27
|
+
# package version file
|
|
28
|
+
lexsi_sdk/version.py
|
|
29
|
+
/workingtest/
|
lexsi_sdk-0.1.12/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Lexsi.ai
|
|
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,95 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lexsi-sdk
|
|
3
|
+
Version: 0.1.12
|
|
4
|
+
Summary: Full stack ML Observability with Lexsi.ai
|
|
5
|
+
Author-email: Lexsi Labs <support@lexsi.ai>
|
|
6
|
+
License: Lexsi Labs Source Available License (LSAL) v1.0
|
|
7
|
+
Keywords: lexsi,lexsi-sdk,lexsi_sdk,ML observability,observability,machine learning
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Intended Audience :: Science/Research
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
16
|
+
Requires-Python: >=3.8
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: pydantic>=1.10.13
|
|
19
|
+
Requires-Dist: httpx
|
|
20
|
+
Requires-Dist: pyjwt>=2.3.0
|
|
21
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
22
|
+
Requires-Dist: requests>=2.31.0
|
|
23
|
+
Requires-Dist: pandas>=1.5.3
|
|
24
|
+
Requires-Dist: plotly>=5.15.0
|
|
25
|
+
Requires-Dist: ipython>=7.34.0
|
|
26
|
+
Requires-Dist: Pillow>=10.4.0
|
|
27
|
+
Requires-Dist: google-genai>=1.24.0
|
|
28
|
+
Requires-Dist: anthropic>=0.57.1
|
|
29
|
+
Requires-Dist: openai>=1.93.1
|
|
30
|
+
Requires-Dist: groq>=0.29.0
|
|
31
|
+
Requires-Dist: xai-sdk>=1.0.0
|
|
32
|
+
Requires-Dist: together>=1.3.14
|
|
33
|
+
Requires-Dist: replicate>=1.0.7
|
|
34
|
+
Requires-Dist: huggingface-hub>=0.33.2
|
|
35
|
+
Requires-Dist: mistralai>=1.9.1
|
|
36
|
+
Requires-Dist: boto3>=1.39.4
|
|
37
|
+
Requires-Dist: opentelemetry-sdk>=1.36.0
|
|
38
|
+
Requires-Dist: opentelemetry-exporter-otlp>=1.36.0
|
|
39
|
+
Requires-Dist: openinference-instrumentation-langchain>=0.1.50
|
|
40
|
+
Requires-Dist: openinference-instrumentation-autogen-agentchat>=0.1.4
|
|
41
|
+
Requires-Dist: openinference-instrumentation-crewai>=0.1.11
|
|
42
|
+
Requires-Dist: openinference-instrumentation-dspy>=0.1.28
|
|
43
|
+
Requires-Dist: openinference-instrumentation-llama-index>=4.3.4
|
|
44
|
+
Requires-Dist: openinference-instrumentation-openai-agents>=1.2.0
|
|
45
|
+
Requires-Dist: openinference-instrumentation-smolagents>=0.1.16
|
|
46
|
+
|
|
47
|
+
## [Lexsi ai](https://lexsi.ai): ML Observability for mission-critical ‘AI’
|
|
48
|
+
|
|
49
|
+
Lexsi Ai is a full-stack ML Observability platform that integrates with your MLOPs platform to Explain, Monitor, Audit and Improve your ML models.
|
|
50
|
+
|
|
51
|
+
Lexsi Ai has multiple components to address the complex observability required for mission-critical ‘AI’.
|
|
52
|
+
|
|
53
|
+
1. **ML Explainability:** Lexsi Ai offers diverse explainability options like- Bactrace(Specialized for deep learning models), SHAPE, Decision View, Observations (New way to correlate expert functioning vs model functioning) and Similar Cases (reference as explanations).
|
|
54
|
+
2. **ML Monitoring:** Monitor your models for drifts, performance & bias. The tool offers diverse options for drift (data/model) like - PSI, KL Divergence, Chi-square test,
|
|
55
|
+
3. **Synthetic ‘AI’:** Deploy advanced synthetic ‘AI’ techniques like GPT-2 & GANs on your tabular data to generate high-quality synthetic datasets. Test the quality and privacy of these data sets using our Anonymity tests, column tests etc.
|
|
56
|
+
4. **ML Risk policies:** Define advanced risk policies on your models.
|
|
57
|
+
5. **AutoML:** Lexsi Ai also provides fully low-code and no-code options to build ML models on your data. For advanced users, it also provides more options to fine-tune it.
|
|
58
|
+
|
|
59
|
+
Lexsi Ai also acts as a common workflow and provides insights acceptable by all stakeholders - Data Science, IT, Risk, Operations and compliance teams, making the rollout and maintenance of AI/ML models seamless and clutter-free.
|
|
60
|
+
|
|
61
|
+
### Quickstart:
|
|
62
|
+
Get started with Lexsi Ai with a few easy steps:
|
|
63
|
+
|
|
64
|
+
1. Sign up and log in to your new Lexsi Ai account.
|
|
65
|
+
2. After logging in, generate an Access Token for your user account.
|
|
66
|
+
3. Set the environment variable **XAI_ACCESS_TOKEN** with the generated value.
|
|
67
|
+
|
|
68
|
+
Once you've completed these steps, you're all set! Now, you can easily log in and start using the Lexsi Ai SDK:
|
|
69
|
+
|
|
70
|
+
1. Log in by importing the "xai" object instance from the "lexsi_sdk" package.
|
|
71
|
+
2. Call the "login" method. This method automatically takes the access token value from the "XAI_ACCESS_TOKEN" environment variable and stores the JWT in the object instance. This means that all your future SDK operations will be authorized automatically, making it simple and hassle-free!
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
from lexsi_sdk import xai as lexsi
|
|
76
|
+
|
|
77
|
+
## login() function authenticates user using token that can be generated in app.lexsi_sdk.com/sdk
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
lexsi.login()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
Enter your Lexsi ai Access Token: ··········
|
|
84
|
+
Authenticated successfully.
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Cookbook:
|
|
88
|
+
In this section, you can review the examples of implementation of Lexsi Ai-SDK.
|
|
89
|
+
|
|
90
|
+
1. [Full features overview of Lexsi Ai](https://colab.research.google.com/drive/1Dy5eL-FJVnFV0K5yOfGGVoAmiS_Icaz3?usp=sharing)
|
|
91
|
+
2. Using Lexsi Ai in Loan Underwriting (Coming Soon)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
### Contribution guidelines:
|
|
95
|
+
At Lexsi Ai, we're passionate about open source and value community contributions! Explore our contribution guide for insights into the development workflow and Lexsi Ai library internals. For bug reports or feature requests, head to GitHub Issues or reach out to us at [support@Lexsi Ai.com](support@Lexsi Ai.com).
|
|
File without changes
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import httpx
|
|
3
|
+
from lexsi_sdk.common.xai_uris import LOGIN_URI
|
|
4
|
+
import jwt
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
import json
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class APIClient(BaseModel):
|
|
10
|
+
"""API client to interact with Lexsi Ai services"""
|
|
11
|
+
|
|
12
|
+
debug: bool = False
|
|
13
|
+
base_url: str = ""
|
|
14
|
+
access_token: str = ""
|
|
15
|
+
auth_token: str = ""
|
|
16
|
+
headers: dict = {}
|
|
17
|
+
|
|
18
|
+
def __init__(self, **kwargs):
|
|
19
|
+
"""Initialize the API client with provided configuration."""
|
|
20
|
+
super().__init__(**kwargs)
|
|
21
|
+
|
|
22
|
+
def get_auth_token(self) -> str:
|
|
23
|
+
"""get jwt auth token value
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
str: jwt auth token
|
|
27
|
+
"""
|
|
28
|
+
return self.auth_token
|
|
29
|
+
|
|
30
|
+
def set_auth_token(self, auth_token):
|
|
31
|
+
"""sets jwt auth token value
|
|
32
|
+
|
|
33
|
+
:param auth_token: jwt auth token
|
|
34
|
+
"""
|
|
35
|
+
self.auth_token = auth_token
|
|
36
|
+
|
|
37
|
+
def set_access_token(self, access_token):
|
|
38
|
+
"""sets access token value
|
|
39
|
+
|
|
40
|
+
:param auth_token: jwt auth token
|
|
41
|
+
"""
|
|
42
|
+
self.access_token = access_token
|
|
43
|
+
|
|
44
|
+
def get_url(self, uri) -> str:
|
|
45
|
+
"""get url by appending uri to base url
|
|
46
|
+
|
|
47
|
+
:param uri: uri of endpoint
|
|
48
|
+
:return: url
|
|
49
|
+
"""
|
|
50
|
+
return f"{self.base_url}/{uri}"
|
|
51
|
+
|
|
52
|
+
def update_headers(self, auth_token):
|
|
53
|
+
"""sets jwt auth token and updates headers for all requests"""
|
|
54
|
+
self.set_auth_token(auth_token)
|
|
55
|
+
self.headers = {
|
|
56
|
+
"Authorization": f"Bearer {self.auth_token}",
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
def refresh_bearer_token(self):
|
|
60
|
+
"""Refresh the bearer token if the current token is expired."""
|
|
61
|
+
try:
|
|
62
|
+
if self.auth_token:
|
|
63
|
+
jwt.decode(
|
|
64
|
+
self.auth_token,
|
|
65
|
+
options={"verify_signature": False, "verify_exp": True},
|
|
66
|
+
)
|
|
67
|
+
except jwt.exceptions.ExpiredSignatureError as e:
|
|
68
|
+
response = self.base_request(
|
|
69
|
+
"POST", LOGIN_URI, {"access_token": self.access_token}
|
|
70
|
+
).json()
|
|
71
|
+
self.update_headers(response["access_token"])
|
|
72
|
+
|
|
73
|
+
def base_request(self, method, uri, payload={}, files=None):
|
|
74
|
+
"""makes request to xai base service
|
|
75
|
+
|
|
76
|
+
:param uri: api uri
|
|
77
|
+
:param method: GET, POST, PUT, DELETE
|
|
78
|
+
:raises Exception: Request exception
|
|
79
|
+
:return: JSON response
|
|
80
|
+
"""
|
|
81
|
+
url = f"{self.base_url}/{uri}"
|
|
82
|
+
try:
|
|
83
|
+
# response = requests.request(
|
|
84
|
+
# method,
|
|
85
|
+
# url,
|
|
86
|
+
# headers=self.headers,
|
|
87
|
+
# json=payload,
|
|
88
|
+
# files=files,
|
|
89
|
+
# stream=stream,
|
|
90
|
+
# )
|
|
91
|
+
|
|
92
|
+
with httpx.Client(http2=True, timeout=None) as client:
|
|
93
|
+
response = client.request(
|
|
94
|
+
method=method,
|
|
95
|
+
url=url,
|
|
96
|
+
headers=self.headers,
|
|
97
|
+
json=payload,
|
|
98
|
+
files=files or None,
|
|
99
|
+
)
|
|
100
|
+
#response.raise_for_status()
|
|
101
|
+
#return response
|
|
102
|
+
|
|
103
|
+
res = None
|
|
104
|
+
try:
|
|
105
|
+
res = response.json().get("details") or response.json()
|
|
106
|
+
except Exception:
|
|
107
|
+
res = response.text
|
|
108
|
+
if 400 <= response.status_code < 500:
|
|
109
|
+
raise Exception(res)
|
|
110
|
+
elif 500 <= response.status_code < 600:
|
|
111
|
+
raise Exception(res)
|
|
112
|
+
else:
|
|
113
|
+
return response
|
|
114
|
+
except Exception as e:
|
|
115
|
+
raise e
|
|
116
|
+
|
|
117
|
+
def request(self, method, uri, payload):
|
|
118
|
+
"""Refresh credentials and dispatch a base request."""
|
|
119
|
+
self.refresh_bearer_token()
|
|
120
|
+
response = self.base_request(method, uri, payload)
|
|
121
|
+
return response
|
|
122
|
+
|
|
123
|
+
def get(self, uri):
|
|
124
|
+
"""makes get request to xai base service
|
|
125
|
+
|
|
126
|
+
:param uri: api uri
|
|
127
|
+
:raises Exception: Request exception
|
|
128
|
+
:return: JSON response
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
self.refresh_bearer_token()
|
|
132
|
+
response = self.base_request("GET", uri)
|
|
133
|
+
return response.json()
|
|
134
|
+
|
|
135
|
+
def post(self, uri, payload={}):
|
|
136
|
+
"""makes post request to xai base service
|
|
137
|
+
|
|
138
|
+
:param uri: api uri
|
|
139
|
+
:param payload: api payload, defaults to {}
|
|
140
|
+
:raises Exception: Request exception
|
|
141
|
+
:return: JSON response
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
self.refresh_bearer_token()
|
|
145
|
+
response = self.base_request("POST", uri, payload)
|
|
146
|
+
|
|
147
|
+
return response.json()
|
|
148
|
+
|
|
149
|
+
# def stream(self, uri):
|
|
150
|
+
# """makes streaming request to xai base service
|
|
151
|
+
|
|
152
|
+
# :param uri: api uri
|
|
153
|
+
# :param payload: api payload, defaults to {}
|
|
154
|
+
# :raises Exception: Request exception
|
|
155
|
+
# :return: JSON response
|
|
156
|
+
# """
|
|
157
|
+
|
|
158
|
+
# self.refresh_bearer_token()
|
|
159
|
+
# response = self.base_request("GET", uri, stream=True)
|
|
160
|
+
# for res in response.iter_lines(decode_unicode=True):
|
|
161
|
+
# if res:
|
|
162
|
+
# if res.startswith("data: "):
|
|
163
|
+
# res = res.split("data: ")[1]
|
|
164
|
+
# yield json.loads(res)
|
|
165
|
+
|
|
166
|
+
def stream(self, uri):
|
|
167
|
+
"""Server-Sent Events / line-streaming endpoint."""
|
|
168
|
+
self.refresh_bearer_token()
|
|
169
|
+
url = f"{self.base_url}/{uri}"
|
|
170
|
+
# if SSE, this header helps
|
|
171
|
+
headers = {**self.headers, "Accept": "text/event-stream"}
|
|
172
|
+
|
|
173
|
+
with httpx.Client(http2=True, timeout=None) as client:
|
|
174
|
+
# streaming MUST be consumed inside the context
|
|
175
|
+
with client.stream("GET", url, headers=headers) as response:
|
|
176
|
+
response.raise_for_status()
|
|
177
|
+
for line in response.iter_lines(): # no decode_unicode arg in httpx
|
|
178
|
+
if not line:
|
|
179
|
+
continue
|
|
180
|
+
# httpx.iter_lines() yields str lines
|
|
181
|
+
if line.startswith("data: "): # typical SSE prefix
|
|
182
|
+
line = line[6:]
|
|
183
|
+
yield json.loads(line)
|
|
184
|
+
|
|
185
|
+
def file(self, uri, files):
|
|
186
|
+
"""makes multipart request to send files
|
|
187
|
+
|
|
188
|
+
:param uri: api uri
|
|
189
|
+
:param file_path: file path
|
|
190
|
+
:return: JSON response
|
|
191
|
+
"""
|
|
192
|
+
self.refresh_bearer_token()
|
|
193
|
+
response = self.base_request("POST", uri, files=files)
|
|
194
|
+
return response.json()
|
|
File without changes
|