openbb-platform-api 1.3.5__tar.gz → 2.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 (38) hide show
  1. openbb_platform_api-2.0.0/.gitignore +65 -0
  2. openbb_platform_api-1.3.5/README.md → openbb_platform_api-2.0.0/PKG-INFO +158 -259
  3. openbb_platform_api-1.3.5/PKG-INFO → openbb_platform_api-2.0.0/README.md +147 -286
  4. openbb_platform_api-2.0.0/openbb_platform_api/app/__init__.py +25 -0
  5. openbb_platform_api-1.3.5/openbb_platform_api/main.py → openbb_platform_api-2.0.0/openbb_platform_api/app/app.py +150 -54
  6. openbb_platform_api-2.0.0/openbb_platform_api/app/args.py +456 -0
  7. openbb_platform_api-2.0.0/openbb_platform_api/app/bootstrap.py +229 -0
  8. openbb_platform_api-2.0.0/openbb_platform_api/app/config.py +350 -0
  9. openbb_platform_api-2.0.0/openbb_platform_api/app/middleware.py +185 -0
  10. openbb_platform_api-2.0.0/openbb_platform_api/app/spec.py +906 -0
  11. {openbb_platform_api-1.3.5 → openbb_platform_api-2.0.0}/openbb_platform_api/assets/default_apps.json +1 -172
  12. openbb_platform_api-2.0.0/openbb_platform_api/main.py +95 -0
  13. openbb_platform_api-2.0.0/openbb_platform_api/models/__init__.py +20 -0
  14. openbb_platform_api-1.3.5/openbb_platform_api/query_models.py → openbb_platform_api-2.0.0/openbb_platform_api/models/query.py +9 -3
  15. openbb_platform_api-1.3.5/openbb_platform_api/response_models.py → openbb_platform_api-2.0.0/openbb_platform_api/models/response.py +49 -11
  16. openbb_platform_api-2.0.0/openbb_platform_api/query_models.py +11 -0
  17. openbb_platform_api-2.0.0/openbb_platform_api/response_models.py +20 -0
  18. openbb_platform_api-2.0.0/openbb_platform_api/service/__init__.py +31 -0
  19. openbb_platform_api-2.0.0/openbb_platform_api/service/agents_service.py +69 -0
  20. openbb_platform_api-2.0.0/openbb_platform_api/service/apps_service.py +59 -0
  21. openbb_platform_api-2.0.0/openbb_platform_api/service/widgets_service.py +214 -0
  22. openbb_platform_api-2.0.0/openbb_platform_api/utils/__init__.py +14 -0
  23. openbb_platform_api-2.0.0/openbb_platform_api/utils/api.py +83 -0
  24. openbb_platform_api-2.0.0/openbb_platform_api/utils/merge_agents.py +14 -0
  25. openbb_platform_api-2.0.0/openbb_platform_api/utils/merge_apps.py +14 -0
  26. openbb_platform_api-2.0.0/openbb_platform_api/utils/merge_widgets.py +97 -0
  27. openbb_platform_api-2.0.0/openbb_platform_api/utils/network.py +53 -0
  28. {openbb_platform_api-1.3.5 → openbb_platform_api-2.0.0}/openbb_platform_api/utils/openapi.py +239 -43
  29. {openbb_platform_api-1.3.5 → openbb_platform_api-2.0.0}/openbb_platform_api/utils/widgets.py +61 -21
  30. openbb_platform_api-2.0.0/pyproject.toml +123 -0
  31. openbb_platform_api-1.3.5/openbb_platform_api/utils/__init__.py +0 -1
  32. openbb_platform_api-1.3.5/openbb_platform_api/utils/api.py +0 -420
  33. openbb_platform_api-1.3.5/openbb_platform_api/utils/merge_agents.py +0 -55
  34. openbb_platform_api-1.3.5/openbb_platform_api/utils/merge_apps.py +0 -46
  35. openbb_platform_api-1.3.5/openbb_platform_api/utils/merge_widgets.py +0 -129
  36. openbb_platform_api-1.3.5/pyproject.toml +0 -23
  37. {openbb_platform_api-1.3.5 → openbb_platform_api-2.0.0}/openbb_platform_api/__init__.py +0 -0
  38. {openbb_platform_api-1.3.5 → openbb_platform_api-2.0.0}/openbb_platform_api/assets/landing_page.html +0 -0
@@ -0,0 +1,65 @@
1
+ # General
2
+ __pycache__/
3
+ *.pyc
4
+ .DS_Store
5
+ *.env
6
+ .venv
7
+ venv*/
8
+ venv
9
+ .vscode
10
+ *.ipynb
11
+ env/
12
+ venv/
13
+ !notebooks/jupyter/.gitkeep
14
+ .python-version
15
+ .mypy_cache
16
+ .ruff_cache
17
+ .pytest_cache
18
+ iframe_figures/
19
+ exports/*
20
+ .idea
21
+ .coverage
22
+ .scannerwork
23
+ htmlcov
24
+ **/.ipynb_checkpoints
25
+ *.swp
26
+ *.http
27
+ .coverage.*
28
+ *_tests.csv
29
+ *_sdk_audit.csv
30
+ !build/docker/compose.env
31
+ .dccache
32
+ *rome.json
33
+ **/node_modules/*
34
+ .cursorignore
35
+ darts_logs/
36
+ custom_imports/*.csv
37
+ custom_imports/*/*.csv
38
+ cache/
39
+ lightning_logs/
40
+ */mocked_path
41
+ *.pem
42
+
43
+ # CLI
44
+ *.pyo
45
+ **/dist/*
46
+ build/cli
47
+ build/nsis/app
48
+ DMG/*
49
+ *.dmg
50
+ *.sh
51
+ cli/openbb_cli/assets/styles/user/*
52
+
53
+ # Platform
54
+ openbb_platform/core/openbb/package/*
55
+ openbb_platform/core/openbb/.build.lock
56
+ **/assets/*.json.xz
57
+
58
+ # Dev Container env
59
+ obb/*
60
+
61
+ # OpenBB Distribution
62
+ !build/conda/installer/*.sh
63
+ *.pkg
64
+ *.exe
65
+ build/conda/tmp
@@ -1,53 +1,119 @@
1
- # OpenBB Platform API Launcher
1
+ Metadata-Version: 2.5
2
+ Name: openbb-platform-api
3
+ Version: 2.0.0
4
+ Summary: OpenBB Platform API: launcher and widgets builder for the Open Data Platform REST API and OpenBB Workspace custom-backend connector.
5
+ Project-URL: Homepage, https://openbb.co
6
+ Project-URL: Repository, https://github.com/OpenBB-finance/OpenBB
7
+ Project-URL: Documentation, https://docs.openbb.co/odp/python/extensions/interface/openbb-api
8
+ Author-email: OpenBB Team <hello@openbb.co>
9
+ License: Apache-2.0
10
+ Requires-Python: <4,>=3.10
11
+ Requires-Dist: deepdiff>=8.6.2
12
+ Requires-Dist: openbb-core>=2.0.0
13
+ Requires-Dist: tomli>=2.0; python_version < '3.11'
14
+ Description-Content-Type: text/markdown
2
15
 
3
- This package is responsible for launching and configuring an OpenBB Platform environment, or FastAPI instance, to use as an OpenBB Workspace [custom backend](https://docs.openbb.co/workspace/data-integration).
16
+ # OpenBB Platform API
4
17
 
5
- ## Installation
18
+ Launcher and widgets builder for the OpenBB Workspace [custom backend](https://docs.openbb.co/workspace/data-integration). Wraps any FastAPI application with the metadata, exception handling, and `widgets.json` generation that OpenBB Workspace expects — so a regular FastAPI app becomes a Workspace data source with no glue code.
6
19
 
7
- This package is included when you run [`pip install openbb`](https://docs.openbb.co/platform/installation); however, it also works as a standalone package
8
- for creating new backends that are not part of the OpenBB GitHub [repository](https://github.com/OpenBB-finance/OpenBB/).
20
+ > **Full documentation:** [docs.openbb.co/odp/python/extensions/interface/openbb-api](https://docs.openbb.co/odp/python/extensions/interface/openbb-api)
9
21
 
10
- To install as a standalone, use a Python environment between versions 3.9 and 3.12, inclusively.
22
+ ## Install
11
23
 
12
24
  ```sh
13
25
  pip install openbb-platform-api
14
26
  ```
15
27
 
16
- ## Usage
17
-
18
- See the [keyword arguments](#keyword-arguments) section for parameters and descriptions.
28
+ Python ≥ 3.10. Already included when you install [`openbb`](https://docs.openbb.co/platform/installation).
19
29
 
20
30
  ### Launch OpenBB Platform
21
31
 
22
32
  To start the OpenBB Platform API, open a terminal, activate the environment where it is installed, and then enter:
23
33
 
24
- ```
34
+ ```sh
25
35
  openbb-api
26
- ```
27
36
 
28
- This will launch a Fast API instance, via `uvicorn`, at `http://127.0.0.1:6900`
37
+ # Launch your own FastAPI app
38
+ openbb-api --app /path/to/your_app.py
29
39
 
30
- Uvicorn can be configured by adding keyword arguments, see the section [below](#keyword-arguments)
40
+ # Factory function?
41
+ openbb-api --app some_file.py:create_app --factory
31
42
 
32
- ### Launch Custom App
43
+ # Launch as a proxy from an openbb-cli .spec file
44
+ openbb-api --spec /path/to/cli.spec
45
+ ```
46
+
47
+ `widgets.json` is auto-generated from your routes' types, response models, and docstrings. Plotly returns become chart widgets, `BaseModel` returns become tables, scalars become metrics — no manual wiring.
33
48
 
34
- To run your application as an OpenBB Workspace custom backend, add the path to the Python file with the FastAPI instance to the launch command.
49
+ ## Spec-driven proxy mode
50
+
51
+ Generate a spec file with `openbb-cli` against any OpenBB Platform deployment, then launch a Workspace-compatible backend that proxies every command to that upstream:
35
52
 
36
53
  ```sh
37
- openbb-api --app /Users/some_user/path/to/main.py
54
+ # Generate the spec once
55
+ openbb --generate-spec --server https://api.example.com -o cli.spec
56
+
57
+ # Launch the proxy
58
+ openbb-api --spec cli.spec
38
59
  ```
39
60
 
40
- #### Arbitrary Instance Name
61
+ Each command in the spec becomes a FastAPI route at its `url_path`; the launcher forwards every request to the spec's `base_url` (preserving query, body, and non-hop-by-hop headers). `widgets.json` is generated from the spec's parameter and response-schema metadata via the same builder used for in-process apps.
41
62
 
42
- Define the FastAPI instance as an arbitrary name with the `--name` argument.
63
+ Useful for shipping a thin frontend container that talks to a managed backend in another cluster, without bundling `openbb-core` or any provider extensions. `--spec` is mutually exclusive with `--app`.
43
64
 
44
- ```sh
45
- openbb-api --app some_file.py --name my_app
65
+ ### `[spec]` config — credentials and base-URL override
66
+
67
+ A `[spec]` table in `openbb.toml` carries the path plus the bits the file alone can't provide — `base_url` overrides for staging/prod, and `headers` injected on every upstream request.
68
+ Header values support the same `$VAR` substitution as `[env]`, so credentials live in environment variables (or `[env]` entries that read from them) and the TOML just maps them onto upstream header names:
69
+
70
+ ```toml
71
+ [env]
72
+ OPENBB_UPSTREAM_TOKEN = "$GITHUB_TOKEN" # or any orchestrator-injected secret
73
+
74
+ [spec]
75
+ path = "/etc/openbb/cli.spec"
76
+ base_url = "https://prod.example.com" # optional; overrides spec's recorded value
77
+
78
+ [spec.headers]
79
+ Authorization = "Bearer $OPENBB_UPSTREAM_TOKEN"
80
+ X-Tenant = "production"
81
+ ```
82
+
83
+ Config-supplied headers OVERRIDE matching incoming-request headers — `[spec.headers]` is the credential-injection point, so a misbehaving client can't leak its own auth value upstream by sending the same header name.
84
+
85
+ ## Custom HTTP middleware (`[middleware]`)
86
+
87
+ Attach Starlette-style HTTP middleware functions from a config-supplied entrypoint — useful for auth, request logging, tracing, IP allow-listing, response transformation. Each entry is a `"module:async_callable"` reference resolved through the standard import system:
88
+
89
+ ```toml
90
+ [middleware]
91
+ hooks = [
92
+ "my_pkg.middleware:auth_middleware",
93
+ "my_pkg.middleware:request_logger",
94
+ ]
95
+ ```
96
+
97
+ ```python
98
+ # my_pkg/middleware.py
99
+ from fastapi.responses import JSONResponse
100
+
101
+ async def auth_middleware(request, call_next):
102
+ if request.headers.get("X-API-Key") != "expected":
103
+ return JSONResponse({"error": "unauthorized"}, status_code=401)
104
+ return await call_next(request)
105
+
106
+ async def request_logger(request, call_next):
107
+ response = await call_next(request)
108
+ print(f"{request.method} {request.url.path} → {response.status_code}")
109
+ return response
46
110
  ```
47
111
 
48
- #### Factory Flag
112
+ List order is **outermost-to-innermost**: the first entry sees the request first on the way in and the response last on the way out. Misconfigured references (missing module, wrong attribute, sync function, wrong arity) raise loudly at startup so deployments fail fast instead of silently passing requests through unauthenticated.
49
113
 
50
- If the FastAPI instance is served via a factory function, set the `--factory` flag.
114
+ ## Single-file launch
115
+
116
+ Everything in this README — app source (`--app` or `--spec`), env injection, host/port, SSL, agents, middleware, credentials — can be set in one `openbb.toml` and launched without any other CLI flags:
51
117
 
52
118
  ```sh
53
119
  openbb-api --app some_file.py:main --factory
@@ -59,38 +125,41 @@ The behavior of the script can be configured with the use of arguments and keywo
59
125
 
60
126
  Launcher specific arguments:
61
127
 
62
- --app Absolute path to the Python file with the target FastAPI instance. Default is the installed OpenBB Platform API.
63
- --name Name of the FastAPI instance in the app file. Default is 'app'.
64
- --factory Flag to indicate if the app name is a factory function. Default is 'false'.
65
- --editable Flag to make widgets.json an editable file that can be modified during runtime. Default is 'false'.
66
- --build If the file already exists, changes prompt action to overwrite/append/ignore. Only valid when --editable true.
67
- --no-build Do not build the widgets.json file. Use this flag to load an existing widgets.json file without checking for updates.
68
- --exclude JSON encoded list of API paths to exclude from widgets.json. Disable entire routes with '*' - e.g. '["/api/v1/*"]'.
69
- --no-filter Do not filter out widgets in widget_settings.json file.
70
- --widgets-json Absolute/relative path to use as the widgets.json file. Default is ~/envs/{env}/assets/widgets.json, when --editable is 'true'.
71
- --apps-json Absolute/relative path to use as the apps.json file. Default is ~/OpenBBUserData/workspace_apps.json.
72
- --agents-json Absolute/relative path to use as the agents.json file. Including this will add the /agents endpoint to the API.
73
-
128
+ ```text
129
+ --app Absolute path to the Python file with the target FastAPI instance. Default is the installed OpenBB Platform API.
130
+ --name Name of the FastAPI instance in the app file. Default is 'app'.
131
+ --factory Flag to indicate if the app name is a factory function. Default is 'false'.
132
+ --editable Flag to make widgets.json an editable file that can be modified during runtime. Default is 'false'.
133
+ --build If the file already exists, changes prompt action to overwrite/append/ignore. Only valid when --editable true.
134
+ --no-build Do not build the widgets.json file. Use this flag to load an existing widgets.json file without checking for updates.
135
+ --exclude JSON encoded list of API paths to exclude from widgets.json. Disable entire routes with '*' - e.g. '["/api/v1/*"]'.
136
+ --no-filter Do not filter out widgets in widget_settings.json file.
137
+ --widgets-json Absolute/relative path to use as the widgets.json file. Default is ~/envs/{env}/assets/widgets.json, when --editable is 'true'.
138
+ --apps-json Absolute/relative path to use as the apps.json file. Default is ~/OpenBBUserData/workspace_apps.json.
139
+ --agents-json Absolute/relative path to use as the agents.json file. Including this will add the /agents endpoint to the API.
140
+ ```
74
141
 
75
142
  All other arguments will be passed to uvicorn. Here are the most common ones:
76
143
 
77
- --host TEXT Host IP address or hostname.
78
- [default: 127.0.0.1]
79
- --port INTEGER Port number.
80
- [default: 6900]
81
- --ssl_keyfile TEXT SSL key file.
82
- --ssl_certfile TEXT SSL certificate file.
83
- --ssl_keyfile_password TEXT SSL keyfile password.
84
- --ssl_version INTEGER SSL version to use.
85
- (see stdlib ssl module's)
86
- [default: 17]
87
- --ssl_cert_reqs INTEGER Whether client certificate is required.
88
- (see stdlib ssl module's)
89
- [default: 0]
90
- --ssl_ca_certs TEXT CA certificates file.
91
- --ssl_ciphers TEXT Ciphers to use.
92
- (see stdlib ssl module's)
93
- [default: TLSv1]
144
+ ```text
145
+ --host TEXT Host IP address or hostname.
146
+ [default: 127.0.0.1]
147
+ --port INTEGER Port number.
148
+ [default: 6900]
149
+ --ssl_keyfile TEXT SSL key file.
150
+ --ssl_certfile TEXT SSL certificate file.
151
+ --ssl_keyfile_password TEXT SSL keyfile password.
152
+ --ssl_version INTEGER SSL version to use.
153
+ (see stdlib ssl module's)
154
+ [default: 17]
155
+ --ssl_cert_reqs INTEGER Whether client certificate is required.
156
+ (see stdlib ssl module's)
157
+ [default: 0]
158
+ --ssl_ca_certs TEXT CA certificates file.
159
+ --ssl_ciphers TEXT Ciphers to use.
160
+ (see stdlib ssl module's)
161
+ [default: TLSv1]
162
+ ```
94
163
 
95
164
  Run `uvicorn --help` to get the full list of arguments.
96
165
 
@@ -121,231 +190,69 @@ Contact the system administrator if you are using a work device and require addi
121
190
 
122
191
  ![This Connection Is Not Private](https://in.norton.com/content/dam/blogs/images/norton/am/this_connection_not_is_private.png)
123
192
 
124
-
125
193
  ## Example Application
126
194
 
127
195
  Examples below will assume this code block is at the start of the file.
128
196
 
129
197
  ```python
130
198
  from fastapi import FastAPI
199
+ from openbb_platform_api.response_models import (
200
+ Data,
201
+ MetricResponseModel,
202
+ OmniWidgetResponseModel,
203
+ PdfResponseModel,
204
+ )
131
205
 
132
206
  app = FastAPI()
133
- ```
134
-
135
- ### Markdown Widget
136
-
137
- This script will create a "markdown" widget with the returned text.
138
207
 
139
- ```python
208
+ # Markdown — return a string
140
209
  @app.get("/hello")
141
210
  async def hello() -> str:
142
- """Widget Description Generated By Docstring"""
143
- return "Hello, from OpenBB!"
144
- ```
145
-
146
- ### Table Widget
147
-
148
- Create a table widget by returning data shaped as a list of dictionaries (records)
149
-
150
- ```python
151
- @app.get("/hello")
152
- async def hello() -> list:
153
- """Widget Description Generated By Docstring"""
154
- return [{"Column 1": "Hello", "Column 2": "from OpenBB!"}]
155
- ```
156
-
157
- ### Metric Widget
158
-
159
- This widget displays a label, value, and optional delta.
160
-
161
- To create a metric widget, import the custom response model below and define it as a return type.
162
-
163
- ```python
164
- from openbb_platform_api.response_models import MetricResponseModel
165
-
166
- @app.get("/hello_metric")
167
- async def hello_metric() -> MetricResponseModel:
168
- """Widget description created by docstring."""
169
- return MetricResponseModel(label="Good Vibes Score", value=100, delta="1%")
170
- ```
171
-
172
- This type of widget can be created as an array of MetricResponseModels. Adjust the response to be a `list[MetricRespnoseModel]`
173
-
174
- ### Query Parameters
175
-
176
- Function arguments will populate as widget parameters.
177
-
178
- ```python
179
- from typing import Literal, Optional
180
-
181
- @app.get("/hello")
182
- async def hello(param1: Optional[str] = None, param2: Literal["Choice 1", "Choice 2"] = None, param3: bool = False) -> str:
183
- """Widget Description Generated By Docstring"""
184
- if not param1 and not param2 and not param3:
185
- return "Enter a parameter or make a choice!"
186
- if param3:
187
- return f"Param3 enabled!"
188
- if param2:
189
- return f"You selected: {param2}"
190
- if param1:
191
- return f"You entered: {param1}"
192
-
193
- return "Nothing to return!"
194
- ```
195
-
196
- ### Easy Date Picker
197
-
198
- Name the parameter "date", or include "_date" in the name, and type it as a string.
199
-
200
- Additionally, a parameter type of `datetime.date` will work.
201
-
202
- ```python
203
- import datetime
204
-
205
- @app.get("/hello_date")
206
- async def hello_date(date: str) -> list:
207
- """Widget description created by docstring."""
208
- # Workspace returns the date as YYYY-MM-DD
209
- return [{"Hello": "Row 1!"}, {"Hello": "Row 2!"}]
210
-
211
-
212
- @app.get("/hello_date_range")
213
- async def hello_date_range(start: datetime.date, end: datetime.date) -> list:
214
- """Widget description created by docstring."""
215
- # Workspace returns the date as YYYY-MM-DD
216
- return [{"Hello": "Row 1!"}, {"Hello": "Row 2!"}]
217
- ```
218
-
219
- This demonstrates how to define any of the basic widget parameter types, in a no-frills way. If you just need something that works, it's an easy starting point.
220
-
221
- ```python
222
- @app.get("/hello_params")
223
- async def hello_params(
224
- required_param: datetime.date,
225
- param_1: str = "Default",
226
- param_2: int = 0,
227
- param_3: float = None,
228
- param_4: Literal["Choice 1", "Choice 2", "Choice 3"] = "Choice 1",
229
- param_5: bool = True,
230
- ) -> list:
231
- """Widget description created by docstring."""
232
- # Handle the "choices" parameter inside the function to convert the displayed label to the desired one.
233
- choices_dict = {"Choice 1": "do_one", "Choice 2": "do_two", "Choice 3": "do_three"}
234
- choice = choices_dict.get(param_4, None)
235
-
236
- # Do something with the parameters and return the result of work.
237
- return [{"Hello": "Row 1!"}, {"Hello": "Row 2!"}]
238
- ```
239
-
240
- ### Annotated Query Params
241
-
242
- Adding helpful placeholder text and tooltips to parameters requires annotating them. This will also help code editors and improve the API documentation.
243
-
244
- Additional settings, compatible with `widgets.json`, are defined in the `json_schema_extra` dictionary, under a key, `x-widget_config`
245
-
246
- ```python
247
- from typing import Annotated
248
- from fastapi import Query
249
- ```
250
-
251
- The pattern for annotating a query parameter is:
252
-
253
- ```python
254
- my_param: Annotated[str, Query(title="My Title", description="My custom hovertext with detailed information")] = None
255
- ```
256
-
257
- ```python
258
- @app.get("/hello_annotated_params")
259
- async def hello_annotated_params(
260
- required_param: Annotated[
261
- datetime.date, Query(description="The date is required.", title="Required Date")
262
- ],
263
- not_required_param: Annotated[
264
- Literal["Choice 1", "Choice 2", "Choice 3"],
265
- Query(
266
- description="Choose from a list of possible choices. The default is, 'Choice 1'",
267
- title="Selector",
268
- json_schema_extra={"x-widget_config": {"multiSelect": True}} # This lets you select multiple items from dropdown choices.
269
- ),
270
- ] = "Choice 1",
271
- ) -> list:
272
- """Widget description created by docstring."""
273
-
274
- # Do something with the parameters and return the result of work.
275
- return [{"Hello": "Row 1!"}, {"Hello": "Row 2!"}]
211
+ """Tooltip from the docstring."""
212
+ return "Hello, OpenBB!"
213
+
214
+ # Table — return a list of records or a typed Data subclass
215
+ @app.get("/rows")
216
+ async def rows() -> list[dict]:
217
+ return [{"symbol": "AAPL", "price": 150.0}]
218
+
219
+ # Metric — single label/value/delta
220
+ @app.get("/score")
221
+ async def score() -> MetricResponseModel:
222
+ return MetricResponseModel(label="Score", value=100, delta="1%")
223
+
224
+ # Chart — return a Plotly figure JSON
225
+ @app.get("/chart", openapi_extra={"widget_config": {"type": "chart"}})
226
+ async def chart() -> dict:
227
+ from plotly.graph_objs import Bar, Figure
228
+ return Figure(data=[Bar(x=["A"], y=[1])]).to_plotly_json()
276
229
  ```
277
230
 
278
- ### Annotated Table Fields
279
-
280
- The procedure for annotating the output is similar to the query parameters, and involves defining a response model.
281
-
282
- A response model is a Data model of Fields. Create one by defining a new class that inherits from "Data", and then define each column as a "Field".
231
+ Annotated `Data` models drive auto-generated table column definitions:
283
232
 
284
233
  ```python
234
+ from datetime import date
285
235
  from openbb_platform_api.response_models import Data
286
236
  from pydantic import Field
287
- ```
288
237
 
289
- Optional values should be defined, as `Optional[{type}]`, with a default value of `None`.
290
-
291
- ```python
292
- class MyData(Data):
293
- """This is a custom Data model."""
294
-
295
- # Add fields to the model.
296
- column_1: datetime.date = Field(
297
- description="The date column is a mandatory field.",
298
- title="Some Date",
299
- )
300
- column_2: Optional[str] = Field(
301
- default=None,
302
- description="This is an optional string column.",
303
- title="Some String",
304
- )
305
- column_3: int = Field(
306
- default=-1,
307
- description="This is an integer column.",
308
- title="Some Integer",
309
- )
310
- column_4: float = Field(
311
- default=10.25,
312
- description="This is a float column.",
313
- title="Some Float",
314
- )
315
- column_5: float = Field(
316
- default=10.25,
317
- description="This is a percent column.",
318
- title="Some Percent",
319
- json_schema_extra={"x-widget_config": {"formatterFn": "percent"}},
320
- )
321
- column_6: float = Field(
322
- default=0.1025,
323
- description="This is a normalized percent value adjusted for presentation.",
324
- title="Some Normalized Percent",
325
- json_schema_extra={
326
- "x-widget_config": {
327
- "formatterFn": "normalizedPercent",
328
- "renderFn": "greenRed",
329
- }
330
- },
238
+ class MyRow(Data):
239
+ when: date = Field(title="Date", description="Trading date")
240
+ pct: float = Field(
241
+ title="Change",
242
+ json_schema_extra={"x-widget_config": {"formatterFn": "percent", "renderFn": "greenRed"}},
331
243
  )
332
244
 
333
-
334
- @app.get("/hello_data")
335
- async def hello_data() -> list[MyData]:
336
- """Widget description created by docstring."""
337
- # Do something with the parameters and return the result of work.
338
- return [MyData(column_1=datetime.date.today(), column_2="Hello!")]
245
+ @app.get("/data")
246
+ async def data() -> list[MyRow]:
247
+ return [MyRow(when=date.today(), pct=0.0125)]
339
248
  ```
340
249
 
341
-
342
250
  ### PDF Widget
343
251
 
344
252
  To create a PDF widget, import the custom response model below and define it as a return type.
345
253
 
346
254
  The model handles conversion of the document, from a bytes object, to a base64 encoded string.
347
255
 
348
-
349
256
  ```python
350
257
  from openbb_platform_api.response_models import PdfResponseModel
351
258
 
@@ -402,7 +309,6 @@ async def open_pdf(
402
309
 
403
310
  To define a chart widget, update the widget "type" and return the content from the `Figure.to_plotly_json()` method.
404
311
 
405
-
406
312
  ```python
407
313
  @app.get(
408
314
  "/hello_chart",
@@ -437,7 +343,6 @@ The entry in `widgets.json` will be automatically created if the conditions belo
437
343
  - POST method takes 1 positional argument, a sub-class of Pydantic BaseModel.
438
344
  - Create a model, like annotated table fields, defining all inputs to the form.
439
345
 
440
-
441
346
  #### Example
442
347
 
443
348
  The code below creates a widget with a form as the input, and an output table of all submitted forms, as processed through the `IntakeForm` model.
@@ -600,7 +505,6 @@ async def create_omni_widget(item: TestOmniWidgetQueryModel):
600
505
 
601
506
  ![Omni Widget](https://github.com/user-attachments/assets/6a5aa886-9701-4448-b397-ed7bab99cac7)
602
507
 
603
-
604
508
  ## Widget Config
605
509
 
606
510
  Any value from the [`widgets.json`](https://docs.openbb.co/terminal/custom-backend/widgets-json-reference) structure can be passed into the `@app` decorator by including an `openapi_extra` dictionary with the key, `"widget_config"`.
@@ -662,7 +566,6 @@ If you would like to construct this file manually, create the file and define th
662
566
  openbb-api --widgets-json /Users/some_user/path/to/widgets.json
663
567
  ```
664
568
 
665
-
666
569
  ### Location of `workspace_apps.json`
667
570
 
668
571
  By default, the location is:
@@ -675,12 +578,8 @@ This can be changed by adding the path as an argument.
675
578
  openbb-api --apps-json /Users/some_user/path/to/workspace_apps.json
676
579
  ```
677
580
 
678
- The OpenBB Workspace allows you to export the current dashboard layout - when it is a custom backend - as a template.
679
-
680
- To export the layout, right-click on the dashboard and select, "Export apps.json".
681
-
682
- A JSON dictionary will be exported. Insert the contents of the export into "~/OpenBBUserData/workspace_apps.json" by pasting between the JSON list markers, [ ].
581
+ The browser will warn about the untrusted cert — accept once, or add `localhost.crt` to the OS trust store.
683
582
 
684
- If there are more than one, add a comma between each dictionary entry.
583
+ ## License
685
584
 
686
- See the page [here](https://docs.openbb.co/workspace/apps#creating-your-own-app) for details on custom backend apps.
585
+ Apache-2.0. © OpenBB.