ivcap-lambda 0.7.22__py3-none-any.whl
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.
- ivcap_lambda/__init__.py +17 -0
- ivcap_lambda/builder.py +260 -0
- ivcap_lambda/decorators.py +60 -0
- ivcap_lambda/executor.py +292 -0
- ivcap_lambda/logger.py +37 -0
- ivcap_lambda/logging.json +77 -0
- ivcap_lambda/mcp.py +261 -0
- ivcap_lambda/py.typed +1 -0
- ivcap_lambda/secret.py +44 -0
- ivcap_lambda/server.py +217 -0
- ivcap_lambda/service_definition.py +44 -0
- ivcap_lambda/utils.py +100 -0
- ivcap_lambda/version.py +22 -0
- ivcap_lambda-0.7.22.dist-info/METADATA +134 -0
- ivcap_lambda-0.7.22.dist-info/RECORD +18 -0
- ivcap_lambda-0.7.22.dist-info/WHEEL +4 -0
- ivcap_lambda-0.7.22.dist-info/licenses/AUTHORS.md +5 -0
- ivcap_lambda-0.7.22.dist-info/licenses/LICENSE +29 -0
ivcap_lambda/utils.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2023 Commonwealth Scientific and Industrial Research Organisation (CSIRO). All rights reserved.
|
|
3
|
+
# Use of this source code is governed by a BSD-style license that can be
|
|
4
|
+
# found in the LICENSE file. See the AUTHORS file for names of contributors.
|
|
5
|
+
#
|
|
6
|
+
from typing import Optional, Dict, Tuple
|
|
7
|
+
|
|
8
|
+
def get_title_from_path(path: str) -> Tuple[str, str]:
|
|
9
|
+
"""Extracts a title from a path string.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
path: A string potentially formatted as a directory path (separated by '/').
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
A tuple containing:
|
|
16
|
+
- The processed string with the first character in lowercase.
|
|
17
|
+
- The processed string with the first character in uppercase.
|
|
18
|
+
"""
|
|
19
|
+
# Extract the last element of the path
|
|
20
|
+
last_element = path.split('/')[-1]
|
|
21
|
+
|
|
22
|
+
# Convert to singular form if plural
|
|
23
|
+
singular = last_element
|
|
24
|
+
if singular.endswith('ies'):
|
|
25
|
+
singular = singular[:-3] + 'y'
|
|
26
|
+
elif singular.endswith('es'):
|
|
27
|
+
singular = singular[:-2]
|
|
28
|
+
elif singular.endswith('ss'):
|
|
29
|
+
pass
|
|
30
|
+
elif singular.endswith('s'):
|
|
31
|
+
singular = singular[:-1]
|
|
32
|
+
|
|
33
|
+
# Replace underscores with spaces
|
|
34
|
+
singular = singular.replace('_', ' ')
|
|
35
|
+
|
|
36
|
+
# Create lowercase and uppercase versions
|
|
37
|
+
if singular:
|
|
38
|
+
lowercase = singular[0].lower() + singular[1:] if len(singular) > 1 else singular.lower()
|
|
39
|
+
uppercase = singular[0].upper() + singular[1:] if len(singular) > 1 else singular.upper()
|
|
40
|
+
else:
|
|
41
|
+
lowercase = ""
|
|
42
|
+
uppercase = ""
|
|
43
|
+
|
|
44
|
+
return (lowercase, uppercase)
|
|
45
|
+
|
|
46
|
+
def find_first(iterable, condition):
|
|
47
|
+
"""
|
|
48
|
+
Returns the first item in the iterable for which the condition is True.
|
|
49
|
+
If no such item is found, returns None.
|
|
50
|
+
"""
|
|
51
|
+
for item in iterable:
|
|
52
|
+
if condition(item):
|
|
53
|
+
return item
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
from fastapi import Request
|
|
57
|
+
from typing import Optional, Dict, Tuple
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_public_url_prefix(req: Request) -> str:
|
|
61
|
+
"""Return the public url prefix for `req`.
|
|
62
|
+
|
|
63
|
+
First checks for a `Forwarded` http header and if
|
|
64
|
+
absent uses the request's `base_ur;` variable.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
req (Request): A FastAPI request instance
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
str: A url as string
|
|
71
|
+
"""
|
|
72
|
+
fw = get_forwarded_header(req)
|
|
73
|
+
if fw != None:
|
|
74
|
+
prefix = f"{fw.get('proto', 'http')}:://{fw.get('for')}"
|
|
75
|
+
else:
|
|
76
|
+
prefix = str(req.base_url).rstrip("/")
|
|
77
|
+
return prefix
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def get_forwarded_header(request: Request) -> Optional[Dict[str, str]]:
|
|
81
|
+
"""
|
|
82
|
+
Parses the "Forwarded" HTTP header according to RFC 7239.
|
|
83
|
+
Returns a dictionary containing the parsed header values, or None if the header is missing.
|
|
84
|
+
"""
|
|
85
|
+
header_value = request.headers.get("Forwarded")
|
|
86
|
+
if not header_value:
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
parsed_values: Dict[str, str] = {}
|
|
90
|
+
for element in header_value.split(";"):
|
|
91
|
+
parts = element.split("=", 1)
|
|
92
|
+
if len(parts) == 2:
|
|
93
|
+
key = parts[0].strip()
|
|
94
|
+
value = parts[1].strip()
|
|
95
|
+
# Remove quotes if present
|
|
96
|
+
if value.startswith('"') and value.endswith('"'):
|
|
97
|
+
value = value[1:-1]
|
|
98
|
+
parsed_values[key] = value
|
|
99
|
+
|
|
100
|
+
return parsed_values
|
ivcap_lambda/version.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2023 Commonwealth Scientific and Industrial Research Organisation (CSIRO). All rights reserved.
|
|
3
|
+
# Use of this source code is governed by a BSD-style license that can be
|
|
4
|
+
# found in the LICENSE file. See the AUTHORS file for names of contributors.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
__version__ = "???"
|
|
8
|
+
try: # Python > 3.8+
|
|
9
|
+
from importlib_metadata import version
|
|
10
|
+
|
|
11
|
+
__version__ = version("ivcap_lambda")
|
|
12
|
+
except ImportError:
|
|
13
|
+
try:
|
|
14
|
+
import pkg_resources
|
|
15
|
+
|
|
16
|
+
__version__ = pkg_resources.get_distribution("ivcap_lambda").version
|
|
17
|
+
except Exception:
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_version():
|
|
22
|
+
return __version__
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ivcap-lambda
|
|
3
|
+
Version: 0.7.22
|
|
4
|
+
Summary: Helper functions for building lambda-style services on the IVCAP platform
|
|
5
|
+
License-File: AUTHORS.md
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Author: Max Ott
|
|
8
|
+
Author-email: max.ott@csiro.au
|
|
9
|
+
Requires-Python: >=3.11,<4.0
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
|
+
Requires-Dist: cachetools (>=5.5.2,<6.0.0)
|
|
16
|
+
Requires-Dist: fastapi (>=0.121.2,<0.122.0)
|
|
17
|
+
Requires-Dist: ivcap-service (>=0.6.21,<0.7.0)
|
|
18
|
+
Requires-Dist: opentelemetry-instrumentation-fastapi (>=0.57b0)
|
|
19
|
+
Requires-Dist: uuid6 (==2024.7.10)
|
|
20
|
+
Requires-Dist: uvicorn (>=0.38.0,<0.39.0)
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# ivcap-lambda: A python library for building lambda-style services on the IVCAP platform
|
|
24
|
+
|
|
25
|
+
> **Package renamed:** `ivcap-ai-tool` has been renamed to `ivcap-lambda` to reflect that
|
|
26
|
+
> the library is useful for any lambda-style IVCAP service, not just AI agent tools.
|
|
27
|
+
> A [compatibility shim](./compat/) is published under the old name — existing apps
|
|
28
|
+
> will continue to work but will see a `DeprecationWarning` prompting migration.
|
|
29
|
+
|
|
30
|
+
<a href="https://scan.coverity.com/projects/ivcap-works-ivcap-ai-tool-sdk-python">
|
|
31
|
+
<img alt="Coverity Scan Build Status"
|
|
32
|
+
src="https://img.shields.io/coverity/scan/31491.svg"/>
|
|
33
|
+
</a>
|
|
34
|
+
|
|
35
|
+
A python library containing various helper and middleware functions
|
|
36
|
+
to simplify developing AI tools to be deployed on IVCAP.
|
|
37
|
+
|
|
38
|
+
> **Note:** A template git repositiory using this library can be found on github
|
|
39
|
+
[ivcap-works/ivcap-python-ai-tool-template](https://github.com/ivcap-works/ivcap-python-ai-tool-template). You may clone that and start from there.
|
|
40
|
+
|
|
41
|
+
## Content
|
|
42
|
+
|
|
43
|
+
* [Register a Tool Function](#register)
|
|
44
|
+
* [Start the Service](#start)
|
|
45
|
+
* [JSON-RPC Middleware](#json-rpc)
|
|
46
|
+
* [Try-Later Middleware](#try-later)
|
|
47
|
+
|
|
48
|
+
### Register a Tool Function <a name="register"></a>
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
class Request(BaseModel):
|
|
52
|
+
jschema: str = Field("urn:sd:schema:some_tool.request.1", alias="$schema")
|
|
53
|
+
...
|
|
54
|
+
|
|
55
|
+
class Result(BaseModel):
|
|
56
|
+
jschema: str = Field("urn:sd:schema:some_tool.1", alias="$schema")
|
|
57
|
+
...
|
|
58
|
+
|
|
59
|
+
def some_tool(req: Request) -> Result:
|
|
60
|
+
"""
|
|
61
|
+
Here should go a quite extensive description of what the tool can be
|
|
62
|
+
used for so that an agent can work out if this tool is useful in
|
|
63
|
+
a specific context.
|
|
64
|
+
|
|
65
|
+
DO NOT ADD PARAMTER AND RETURN DECRIPTIONS -
|
|
66
|
+
DESCRIBE THEM IN THE `Request` MODEL
|
|
67
|
+
"""
|
|
68
|
+
...
|
|
69
|
+
|
|
70
|
+
return Result(...)
|
|
71
|
+
|
|
72
|
+
add_tool_api_route(app, "/", some_tool, opts=ToolOptions(tags=["Great Tool"]))
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Start the Service <a name="start"></a>
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
app = FastAPI(
|
|
79
|
+
..
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
if __name__ == "__main__":
|
|
83
|
+
start_tool_server(app, some_tool)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### JSON-RPC Middleware <a name="json-rpc"></a>
|
|
87
|
+
|
|
88
|
+
This middleware will convert any `POST /` with a payload
|
|
89
|
+
following the [JSON-RPC](https://www.jsonrpc.org/specification)
|
|
90
|
+
specification to an internal `POST /{method}` and will return
|
|
91
|
+
the result formatted according to the JSON-RPC spec.
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from ivcap_fastapi import use_json_rpc_middleware
|
|
95
|
+
|
|
96
|
+
app = FastAPI(
|
|
97
|
+
..
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
use_json_rpc_middleware(app)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Try-Later Middleware <a name="try-later"></a>
|
|
104
|
+
|
|
105
|
+
This middleware is supporting the use case where the execution of a
|
|
106
|
+
requested service is taking longer than the caller is willing to wait.
|
|
107
|
+
A typical use case is where the service is itself outsourcing the execution
|
|
108
|
+
to some other long-running service but may immediately receive a reference
|
|
109
|
+
to the eventual result.
|
|
110
|
+
|
|
111
|
+
In this case, raising a `TryLaterException` will return with a 204
|
|
112
|
+
status code and additional information on how to later check back for the
|
|
113
|
+
result.
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from ivcap_fastapi import TryLaterException, use_try_later_middleware
|
|
117
|
+
use_try_later_middleware(app)
|
|
118
|
+
|
|
119
|
+
@app.post("/big_job")
|
|
120
|
+
def big_job(req: Request) -> Response:
|
|
121
|
+
jobID, expected_exec_time = scheduling_big_job(req)
|
|
122
|
+
raise TryLaterException(f"/big_job/jobs/{jobID}", expected_exec_time)
|
|
123
|
+
|
|
124
|
+
@app.get("/big_job/jobs/{jobID}")
|
|
125
|
+
def get_job(jobID: str) -> Response:
|
|
126
|
+
resp = find_result_for(job_id)
|
|
127
|
+
return resp
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Specifically, raising `TryLaterException(location, delay)` will
|
|
131
|
+
return an HTTP response with a 204 status code with the additional
|
|
132
|
+
HTTP headers `Location` and `Retry-Later` set to `location` and
|
|
133
|
+
`delay` respectively.
|
|
134
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
ivcap_lambda/__init__.py,sha256=vQouZe1fq35W5UC2621EPOOni_xalbp9Zm6yxSDiPMI,777
|
|
2
|
+
ivcap_lambda/builder.py,sha256=MEcVs4Ey-WC6yRMIIzi-Phww8je37ToacVtKjt4VvHk,10779
|
|
3
|
+
ivcap_lambda/decorators.py,sha256=DgLOqKAYVh1QvJNDcsFqoIzXloagIflBeBO-WeNdy0U,2813
|
|
4
|
+
ivcap_lambda/executor.py,sha256=RwpZq9-Sm2RBUXqnskQUr580KF4zt9r0Vcm60_mF3uY,11154
|
|
5
|
+
ivcap_lambda/logger.py,sha256=yVioKPXVUIjJ5WzSA0_cZxr4jthM8K7edO3px29rnLA,1327
|
|
6
|
+
ivcap_lambda/logging.json,sha256=EBreTGZe9l1OU_MylU4uigwA8Zs_RBPCr_UDWHCVAps,1642
|
|
7
|
+
ivcap_lambda/mcp.py,sha256=WJgLLD3sWxd9vxhHnqPTf0zMENgNgaCCJEjDKZ162hU,8250
|
|
8
|
+
ivcap_lambda/py.typed,sha256=8ZJUsxZiuOy1oJeVhsTWQhTG_6pTVHVXk5hJL79ebTk,25
|
|
9
|
+
ivcap_lambda/secret.py,sha256=iLXhuA0rfXLIlahVp7htk9_MF5mpfccLa6gqCp-OAEs,1300
|
|
10
|
+
ivcap_lambda/server.py,sha256=17TIO3k9ThTNwoXYbEXkWvTIkPvEdS5esExy4U4Qyd0,6629
|
|
11
|
+
ivcap_lambda/service_definition.py,sha256=ueuvo4Q5TxmBao-zeNP8qfYuVJwdXPAuYaOPGd12DdI,1645
|
|
12
|
+
ivcap_lambda/utils.py,sha256=PhUwlMDO8_IHIPonJo8ZKX9mJimrmRDluhfkDzGGCwY,3145
|
|
13
|
+
ivcap_lambda/version.py,sha256=doCn_9Qf-MMhIb5a7LaVxn3YDfU0SVH2QebEUTYe8zg,610
|
|
14
|
+
ivcap_lambda-0.7.22.dist-info/METADATA,sha256=3i1id6WPuwOaGuCEDz0-MJTIXit9frM2kfbr31UuVy8,4397
|
|
15
|
+
ivcap_lambda-0.7.22.dist-info/WHEEL,sha256=EGEvSphFYqXKs23-kQBeyNoJP1nrT8ZJKQoi5p5DYL8,88
|
|
16
|
+
ivcap_lambda-0.7.22.dist-info/licenses/AUTHORS.md,sha256=s9xR4_HAHQgbNlj505LViebt5AtACQmhPf92aJvNYgg,88
|
|
17
|
+
ivcap_lambda-0.7.22.dist-info/licenses/LICENSE,sha256=dsQrDPPwW7iJs9pxahgJKDW8RNPf5FyXG70MFUlxcuk,1587
|
|
18
|
+
ivcap_lambda-0.7.22.dist-info/RECORD,,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023, Commonwealth Scientific and Industrial Research Organisation (CSIRO) ABN 41 687 119 230
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
* Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|