jims-api 0.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.
@@ -0,0 +1,67 @@
1
+ Metadata-Version: 2.4
2
+ Name: jims-api
3
+ Version: 0.0.0
4
+ Summary: universal API for JIMS application
5
+ Requires-Python: >=3.12
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: click>=8.0
8
+ Requires-Dist: fastapi>=0.128.0
9
+ Requires-Dist: jims-core>=0.4.2
10
+ Requires-Dist: loguru>=0.7.3
11
+ Requires-Dist: uvicorn>=0.35.0
12
+
13
+ # jims-api
14
+
15
+ `jims-api` exposes any JIMS application as a FastAPI HTTP service.
16
+
17
+
18
+ ## Run
19
+
20
+ ```bash
21
+ jims-api --app my_project.app:app --port 8080
22
+ ```
23
+
24
+ Environment variables:
25
+
26
+ - `JIMS_APP` - JIMS app import path (`module:attr`)
27
+ - `JIMS_PORT` - HTTP port
28
+ - `JIMS_HOST` - HTTP host
29
+ - `JIMS_API_KEY` - optional bearer token for auth
30
+
31
+ ## Endpoints
32
+
33
+ - `GET /health`
34
+ - `POST /api/v1/chat`
35
+
36
+ ### `POST /api/v1/chat`
37
+
38
+ Request:
39
+
40
+ ```json
41
+ {
42
+ "contact_id": "customer:42",
43
+ "message": "Hello",
44
+ "thread_id": null,
45
+ "thread_config": {"interface": "api"},
46
+ "event_type": "comm.user_message",
47
+ "run_conversation_start_on_new_thread": false
48
+ }
49
+ ```
50
+
51
+ Response:
52
+
53
+ ```json
54
+ {
55
+ "thread_id": "0194f0f3-d88d-7cca-8f37-ff44f911f539",
56
+ "created_new_thread": true,
57
+ "assistant_messages": ["Hi! How can I help?"],
58
+ "events": [
59
+ {
60
+ "event_type": "comm.assistant_message",
61
+ "event_data": {"role": "assistant", "content": "Hi! How can I help?"}
62
+ }
63
+ ]
64
+ }
65
+ ```
66
+
67
+ If `JIMS_API_KEY` is set, send it as `Authorization: Bearer <token>`
@@ -0,0 +1,55 @@
1
+ # jims-api
2
+
3
+ `jims-api` exposes any JIMS application as a FastAPI HTTP service.
4
+
5
+
6
+ ## Run
7
+
8
+ ```bash
9
+ jims-api --app my_project.app:app --port 8080
10
+ ```
11
+
12
+ Environment variables:
13
+
14
+ - `JIMS_APP` - JIMS app import path (`module:attr`)
15
+ - `JIMS_PORT` - HTTP port
16
+ - `JIMS_HOST` - HTTP host
17
+ - `JIMS_API_KEY` - optional bearer token for auth
18
+
19
+ ## Endpoints
20
+
21
+ - `GET /health`
22
+ - `POST /api/v1/chat`
23
+
24
+ ### `POST /api/v1/chat`
25
+
26
+ Request:
27
+
28
+ ```json
29
+ {
30
+ "contact_id": "customer:42",
31
+ "message": "Hello",
32
+ "thread_id": null,
33
+ "thread_config": {"interface": "api"},
34
+ "event_type": "comm.user_message",
35
+ "run_conversation_start_on_new_thread": false
36
+ }
37
+ ```
38
+
39
+ Response:
40
+
41
+ ```json
42
+ {
43
+ "thread_id": "0194f0f3-d88d-7cca-8f37-ff44f911f539",
44
+ "created_new_thread": true,
45
+ "assistant_messages": ["Hi! How can I help?"],
46
+ "events": [
47
+ {
48
+ "event_type": "comm.assistant_message",
49
+ "event_data": {"role": "assistant", "content": "Hi! How can I help?"}
50
+ }
51
+ ]
52
+ }
53
+ ```
54
+
55
+ If `JIMS_API_KEY` is set, send it as `Authorization: Bearer <token>`
@@ -0,0 +1,38 @@
1
+ [project]
2
+ name = "jims-api"
3
+ dynamic = ["version"]
4
+ description = "universal API for JIMS application"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ dependencies = [
8
+ "click>=8.0",
9
+ "fastapi>=0.128.0",
10
+ "jims-core>=0.4.2",
11
+ "loguru>=0.7.3",
12
+ "uvicorn>=0.35.0",
13
+ ]
14
+
15
+ [tool.uv.sources]
16
+ jims-core = { workspace = true }
17
+
18
+ [tool.uv-workspace-codegen]
19
+ generate = true
20
+ template_type = ["lib", "publish"]
21
+ generate_standard_pytest_step = true
22
+
23
+ [tool.pytest.ini_options]
24
+ asyncio_mode = "auto"
25
+ testpaths = ["tests"]
26
+
27
+ [dependency-groups]
28
+ dev = [
29
+ "aiosqlite>=0.20.0",
30
+ "httpx>=0.28.0",
31
+ "mypy>=1.19.1",
32
+ "pytest>=8.4.1",
33
+ "pytest-asyncio>=1.1.0",
34
+ "ruff>=0.15.0",
35
+ ]
36
+
37
+ [project.scripts]
38
+ jims-api = "jims_api.main:main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,184 @@
1
+ import asyncio
2
+ from typing import Any, Awaitable
3
+ from uuid import UUID
4
+
5
+ import click
6
+ import uvicorn
7
+ from fastapi import Depends, FastAPI, Header, HTTPException
8
+ from jims_core.app import JimsApp
9
+ from jims_core.thread.thread_controller import ThreadController
10
+ from jims_core.util import (
11
+ load_jims_app,
12
+ setup_monitoring_and_tracing_with_sentry,
13
+ setup_prometheus_metrics,
14
+ setup_verbose_logging,
15
+ uuid7,
16
+ )
17
+ from loguru import logger
18
+ from pydantic import BaseModel, Field
19
+
20
+
21
+ class ChatRequest(BaseModel):
22
+ contact_id: str
23
+ message: str
24
+ thread_id: UUID | None = None # if none provided - create new thread
25
+ thread_config: dict[str, Any] = Field(default_factory=lambda: {"interface": "api"})
26
+ event_type: str = "comm.user_message"
27
+ run_conversation_start_on_new_thread: bool = False
28
+
29
+
30
+ class ApiEvent(BaseModel):
31
+ event_type: str
32
+ event_data: dict[str, Any]
33
+
34
+
35
+ class ChatResponse(BaseModel):
36
+ thread_id: UUID
37
+ created_new_thread: bool
38
+ assistant_messages: list[str]
39
+ events: list[ApiEvent]
40
+
41
+
42
+ def _extract_token(authorization: str | None) -> str | None:
43
+ if authorization is None:
44
+ return None
45
+
46
+ if authorization.lower().startswith("bearer "):
47
+ return authorization.split(" ", 1)[1]
48
+
49
+ return authorization
50
+
51
+
52
+ def _auth_dependency(api_key: str | None):
53
+ async def require_auth(authorization: str | None = Header(default=None, alias="Authorization")) -> None:
54
+ if api_key is None:
55
+ return
56
+
57
+ token = _extract_token(authorization)
58
+ if token != api_key:
59
+ raise HTTPException(status_code=401, detail="Unauthorized")
60
+
61
+ return require_auth
62
+
63
+
64
+ async def _resolve_jims_app(app_name: str) -> JimsApp:
65
+ loaded_app = load_jims_app(app_name)
66
+ if isinstance(loaded_app, Awaitable):
67
+ loaded_app = await loaded_app
68
+ return loaded_app
69
+
70
+
71
+ def create_api(jims_app: JimsApp, api_key: str | None) -> FastAPI:
72
+ app = FastAPI(title="JIMS API", version="0.1.0")
73
+ require_auth = _auth_dependency(api_key)
74
+
75
+ @app.get("/health")
76
+ async def health() -> dict[str, str]:
77
+ return {"status": "ok"}
78
+
79
+ @app.post("/api/v1/chat", response_model=ChatResponse, dependencies=[Depends(require_auth)])
80
+ async def chat(req: ChatRequest) -> ChatResponse:
81
+ created_new_thread = False
82
+
83
+ if req.thread_id is not None:
84
+ ctl = await ThreadController.from_thread_id(jims_app.sessionmaker, req.thread_id)
85
+ if ctl is None:
86
+ raise HTTPException(status_code=404, detail=f"Thread '{req.thread_id}' not found")
87
+ else:
88
+ ctl = await ThreadController.latest_thread_from_contact_id(jims_app.sessionmaker, req.contact_id)
89
+
90
+ if ctl is None:
91
+ created_new_thread = True
92
+ thread_id = uuid7()
93
+ ctl = await jims_app.new_thread(
94
+ contact_id=req.contact_id,
95
+ thread_id=thread_id,
96
+ thread_config=req.thread_config,
97
+ )
98
+
99
+ if req.run_conversation_start_on_new_thread and jims_app.conversation_start_pipeline is not None:
100
+ await ctl.run_pipeline_with_context(jims_app.conversation_start_pipeline)
101
+
102
+ await ctl.store_event_dict(
103
+ event_id=uuid7(),
104
+ event_type=req.event_type,
105
+ event_data={"role": "user", "content": req.message},
106
+ )
107
+
108
+ try:
109
+ outgoing_events = await ctl.run_pipeline_with_context(jims_app.pipeline)
110
+ except Exception as exc:
111
+ logger.exception("Pipeline execution failed")
112
+ raise HTTPException(status_code=500, detail=f"Pipeline error: {exc}") from exc
113
+
114
+ assistant_messages = [
115
+ str(ev.event_data.get("content", ""))
116
+ for ev in outgoing_events
117
+ if ev.event_type == "comm.assistant_message" and isinstance(ev.event_data, dict)
118
+ ]
119
+
120
+ response_events = [
121
+ ApiEvent(
122
+ event_type=ev.event_type,
123
+ event_data=ev.event_data if isinstance(ev.event_data, dict) else {},
124
+ # todo filter comm.* events only?
125
+ )
126
+ for ev in outgoing_events
127
+ ]
128
+
129
+ return ChatResponse(
130
+ thread_id=ctl.thread.thread_id,
131
+ created_new_thread=created_new_thread,
132
+ assistant_messages=assistant_messages,
133
+ events=response_events,
134
+ )
135
+
136
+ return app
137
+
138
+
139
+ @click.command()
140
+ @click.option("--app", type=click.STRING, default="app", help="JIMS app in module:attr format.")
141
+ @click.option("--host", type=click.STRING, default="0.0.0.0")
142
+ @click.option("--port", type=click.INT, default=8080)
143
+ @click.option("--api-key", type=click.STRING, default=None, help="Optional bearer token for API auth.")
144
+ @click.option("--enable-sentry", is_flag=True, help="Enable tracing to Sentry", default=False)
145
+ @click.option("--metrics-port", type=click.INT, default=8000)
146
+ @click.option("--verbose", is_flag=True, default=False)
147
+ def cli(
148
+ app: str,
149
+ host: str,
150
+ port: int,
151
+ api_key: str | None,
152
+ enable_sentry: bool,
153
+ metrics_port: int,
154
+ verbose: bool,
155
+ ) -> None:
156
+ if verbose:
157
+ setup_verbose_logging()
158
+
159
+ setup_prometheus_metrics(port=metrics_port)
160
+
161
+ if enable_sentry:
162
+ setup_monitoring_and_tracing_with_sentry()
163
+
164
+ async def run_api() -> None:
165
+ jims_app = await _resolve_jims_app(app)
166
+ api = create_api(jims_app, api_key=api_key)
167
+ server = uvicorn.Server(uvicorn.Config(api, host=host, port=port, log_level="info"))
168
+ await server.serve()
169
+
170
+ try:
171
+ asyncio.run(run_api())
172
+ except KeyboardInterrupt:
173
+ logger.info("API stopped by user")
174
+ except Exception as exc:
175
+ logger.exception(f"API crashed: {exc}")
176
+ raise SystemExit(1) from exc
177
+
178
+
179
+ def main() -> None:
180
+ cli(auto_envvar_prefix="JIMS")
181
+
182
+
183
+ if __name__ == "__main__":
184
+ main()
File without changes
@@ -0,0 +1,67 @@
1
+ Metadata-Version: 2.4
2
+ Name: jims-api
3
+ Version: 0.0.0
4
+ Summary: universal API for JIMS application
5
+ Requires-Python: >=3.12
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: click>=8.0
8
+ Requires-Dist: fastapi>=0.128.0
9
+ Requires-Dist: jims-core>=0.4.2
10
+ Requires-Dist: loguru>=0.7.3
11
+ Requires-Dist: uvicorn>=0.35.0
12
+
13
+ # jims-api
14
+
15
+ `jims-api` exposes any JIMS application as a FastAPI HTTP service.
16
+
17
+
18
+ ## Run
19
+
20
+ ```bash
21
+ jims-api --app my_project.app:app --port 8080
22
+ ```
23
+
24
+ Environment variables:
25
+
26
+ - `JIMS_APP` - JIMS app import path (`module:attr`)
27
+ - `JIMS_PORT` - HTTP port
28
+ - `JIMS_HOST` - HTTP host
29
+ - `JIMS_API_KEY` - optional bearer token for auth
30
+
31
+ ## Endpoints
32
+
33
+ - `GET /health`
34
+ - `POST /api/v1/chat`
35
+
36
+ ### `POST /api/v1/chat`
37
+
38
+ Request:
39
+
40
+ ```json
41
+ {
42
+ "contact_id": "customer:42",
43
+ "message": "Hello",
44
+ "thread_id": null,
45
+ "thread_config": {"interface": "api"},
46
+ "event_type": "comm.user_message",
47
+ "run_conversation_start_on_new_thread": false
48
+ }
49
+ ```
50
+
51
+ Response:
52
+
53
+ ```json
54
+ {
55
+ "thread_id": "0194f0f3-d88d-7cca-8f37-ff44f911f539",
56
+ "created_new_thread": true,
57
+ "assistant_messages": ["Hi! How can I help?"],
58
+ "events": [
59
+ {
60
+ "event_type": "comm.assistant_message",
61
+ "event_data": {"role": "assistant", "content": "Hi! How can I help?"}
62
+ }
63
+ ]
64
+ }
65
+ ```
66
+
67
+ If `JIMS_API_KEY` is set, send it as `Authorization: Bearer <token>`
@@ -0,0 +1,11 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/jims_api/__init__.py
4
+ src/jims_api/main.py
5
+ src/jims_api/py.typed
6
+ src/jims_api.egg-info/PKG-INFO
7
+ src/jims_api.egg-info/SOURCES.txt
8
+ src/jims_api.egg-info/dependency_links.txt
9
+ src/jims_api.egg-info/entry_points.txt
10
+ src/jims_api.egg-info/requires.txt
11
+ src/jims_api.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ jims-api = jims_api.main:main
@@ -0,0 +1,5 @@
1
+ click>=8.0
2
+ fastapi>=0.128.0
3
+ jims-core>=0.4.2
4
+ loguru>=0.7.3
5
+ uvicorn>=0.35.0
@@ -0,0 +1 @@
1
+ jims_api