50 changed files with 1196 additions and 1158 deletions
@ -1,18 +1,20 @@ |
|||
name: Add to Project |
|||
|
|||
on: |
|||
pull_request_target: |
|||
pull_request_target: # zizmor: ignore[dangerous-triggers] |
|||
issues: |
|||
types: |
|||
- opened |
|||
- reopened |
|||
|
|||
permissions: {} |
|||
|
|||
jobs: |
|||
add-to-project: |
|||
name: Add to project |
|||
runs-on: ubuntu-latest |
|||
steps: |
|||
- uses: actions/[email protected] |
|||
- uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2 |
|||
with: |
|||
project-url: https://github.com/orgs/fastapi/projects/2 |
|||
github-token: ${{ secrets.PROJECTS_TOKEN }} |
|||
github-token: ${{ secrets.PROJECTS_TOKEN }} # zizmor: ignore[secrets-outside-env] |
|||
|
|||
@ -9,25 +9,26 @@ on: |
|||
issues: |
|||
types: |
|||
- labeled |
|||
pull_request_target: |
|||
pull_request_target: # zizmor: ignore[dangerous-triggers] |
|||
types: |
|||
- labeled |
|||
workflow_dispatch: |
|||
|
|||
permissions: |
|||
issues: write |
|||
pull-requests: write |
|||
permissions: {} |
|||
|
|||
jobs: |
|||
issue-manager: |
|||
if: github.repository_owner == 'fastapi' |
|||
runs-on: ubuntu-latest |
|||
permissions: |
|||
issues: write |
|||
pull-requests: write |
|||
steps: |
|||
- name: Dump GitHub context |
|||
env: |
|||
GITHUB_CONTEXT: ${{ toJson(github) }} |
|||
run: echo "$GITHUB_CONTEXT" |
|||
- uses: tiangolo/[email protected] |
|||
- uses: tiangolo/issue-manager@2fb3484ec9279485df8659e8ec73de262431737d # 0.6.0 |
|||
with: |
|||
token: ${{ secrets.GITHUB_TOKEN }} |
|||
config: > |
|||
|
|||
@ -1,7 +1,7 @@ |
|||
name: Latest Changes |
|||
|
|||
on: |
|||
pull_request_target: |
|||
pull_request_target: # zizmor: ignore[dangerous-triggers] |
|||
branches: |
|||
- master |
|||
types: |
|||
@ -16,27 +16,29 @@ on: |
|||
required: false |
|||
default: 'false' |
|||
|
|||
permissions: {} |
|||
|
|||
jobs: |
|||
latest-changes: |
|||
runs-on: ubuntu-latest |
|||
if: github.event_name == 'workflow_dispatch' || github.event.pull_request.merged == true |
|||
steps: |
|||
- name: Dump GitHub context |
|||
env: |
|||
GITHUB_CONTEXT: ${{ toJson(github) }} |
|||
run: echo "$GITHUB_CONTEXT" |
|||
# pin to actions/checkout@v5 for compatibility with latest-changes |
|||
# Ref: https://github.com/actions/checkout/issues/2313 |
|||
- uses: actions/checkout@v5 |
|||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 |
|||
with: |
|||
# To allow latest-changes to commit to the main branch |
|||
token: ${{ secrets.FASTAPI_LATEST_CHANGES }} |
|||
token: ${{ secrets.FASTAPI_LATEST_CHANGES }} # zizmor: ignore[secrets-outside-env] |
|||
persist-credentials: true # required by tiangolo/latest-changes |
|||
# Allow debugging with tmate |
|||
- name: Setup tmate session |
|||
uses: mxschmitt/action-tmate@v3 |
|||
uses: mxschmitt/action-tmate@c0afd6f790e3a5564914980036ebf83216678101 # v3.23 |
|||
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }} |
|||
with: |
|||
limit-access-to-actor: true |
|||
- uses: tiangolo/[email protected] |
|||
- uses: tiangolo/latest-changes@c9d329cb147f0ddf4fb631214e3f838ff17ccbbd # 0.4.1 |
|||
with: |
|||
token: ${{ secrets.GITHUB_TOKEN }} |
|||
latest_changes_file: docs/en/docs/release-notes.md |
|||
|
|||
@ -6,6 +6,8 @@ on: |
|||
- opened |
|||
- synchronize |
|||
|
|||
permissions: {} |
|||
|
|||
env: |
|||
# Forks and Dependabot don't have access to secrets |
|||
HAS_SECRETS: ${{ secrets.PRE_COMMIT != '' }} |
|||
@ -18,7 +20,7 @@ jobs: |
|||
env: |
|||
GITHUB_CONTEXT: ${{ toJson(github) }} |
|||
run: echo "$GITHUB_CONTEXT" |
|||
- uses: actions/checkout@v5 |
|||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 |
|||
name: Checkout PR for own repo |
|||
if: env.HAS_SECRETS == 'true' |
|||
with: |
|||
@ -28,22 +30,25 @@ jobs: |
|||
# And it needs the full history to be able to compute diffs |
|||
fetch-depth: 0 |
|||
# A token other than the default GITHUB_TOKEN is needed to be able to trigger CI |
|||
token: ${{ secrets.PRE_COMMIT }} |
|||
token: ${{ secrets.PRE_COMMIT }} # zizmor: ignore[secrets-outside-env] |
|||
persist-credentials: true # Required for `git push` command |
|||
# pre-commit lite ci needs the default checkout configs to work |
|||
- uses: actions/checkout@v5 |
|||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 |
|||
name: Checkout PR for fork |
|||
if: env.HAS_SECRETS == 'false' |
|||
with: |
|||
# To be able to commit it needs the head branch of the PR, the remote one |
|||
ref: ${{ github.event.pull_request.head.sha }} |
|||
fetch-depth: 0 |
|||
persist-credentials: false |
|||
- name: Set up Python |
|||
uses: actions/setup-python@v6 |
|||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 |
|||
with: |
|||
python-version-file: ".python-version" |
|||
- name: Setup uv |
|||
uses: astral-sh/setup-uv@v7 |
|||
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 |
|||
with: |
|||
version: "0.11.4" |
|||
cache-dependency-glob: | |
|||
pyproject.toml |
|||
uv.lock |
|||
@ -51,7 +56,7 @@ jobs: |
|||
run: uv sync --locked --extra all |
|||
- name: Run prek - pre-commit |
|||
id: precommit |
|||
run: uvx prek run --from-ref origin/${GITHUB_BASE_REF} --to-ref HEAD --show-diff-on-failure |
|||
run: uv run prek run --from-ref origin/${GITHUB_BASE_REF} --to-ref HEAD --show-diff-on-failure |
|||
continue-on-error: true |
|||
- name: Commit and push changes |
|||
if: env.HAS_SECRETS == 'true' |
|||
@ -65,7 +70,7 @@ jobs: |
|||
git commit -m "🎨 Auto format" |
|||
git push |
|||
fi |
|||
- uses: pre-commit-ci/[email protected] |
|||
- uses: pre-commit-ci/lite-action@5d6cc0eb514c891a40562a58a8e71576c5c7fb43 # v1.1.0 |
|||
if: env.HAS_SECRETS == 'false' |
|||
with: |
|||
msg: 🎨 Auto format |
|||
@ -85,6 +90,6 @@ jobs: |
|||
GITHUB_CONTEXT: ${{ toJson(github) }} |
|||
run: echo "$GITHUB_CONTEXT" |
|||
- name: Decide whether the needed jobs succeeded or failed |
|||
uses: re-actors/alls-green@release/v1 |
|||
uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2 |
|||
with: |
|||
jobs: ${{ toJSON(needs) }} |
|||
|
|||
@ -1,44 +0,0 @@ |
|||
# Vibe Coding { #vibe-coding } |
|||
|
|||
Are you tired of all that **data validation**, **documentation**, **serialization**, and all that **boring** stuff? |
|||
|
|||
Do you just want to **vibe**? 🎶 |
|||
|
|||
**FastAPI** now supports a new `@app.vibe()` decorator that embraces **modern AI coding best practices**. 🤖 |
|||
|
|||
## How It Works { #how-it-works } |
|||
|
|||
The `@app.vibe()` decorator is intended to receive **any HTTP method** (`GET`, `POST`, `PUT`, `DELETE`, `PATCH`, etc.) and **any payload**. |
|||
|
|||
The body should be annotated with `Any`, because the request and the response would be... well... **anything**. 🤷 |
|||
|
|||
The idea is that you would receive the payload and send it **directly** to an LLM provider, using a `prompt` to tell the LLM what to do, and return the response **as is**. No questions asked. |
|||
|
|||
You don't even need to write the body of the function. The `@app.vibe()` decorator does everything for you based on AI vibes: |
|||
|
|||
{* ../../docs_src/vibe/tutorial001_py310.py hl[8:12] *} |
|||
|
|||
## Benefits { #benefits } |
|||
|
|||
By using `@app.vibe()`, you get to enjoy: |
|||
|
|||
* **Freedom**: No data validation. No schemas. No constraints. Just vibes. ✨ |
|||
* **Flexibility**: The request can be anything. The response can be anything. Who needs types anyway? |
|||
* **No documentation**: Why document your API when an LLM can figure it out? Auto-generated OpenAPI docs are *so* 2020. |
|||
* **No serialization**: Just pass the raw, unstructured data around. Serialization is for people who don't trust their LLMs. |
|||
* **Embrace modern AI coding practices**: Leave everything up to an LLM to decide. The model knows best. Always. |
|||
* **No code reviews**: There's no code to review. No PRs to approve. No comments to address. Embrace vibe coding fully, replace the theater of approving and merging vibe coded PRs that no one looks at with full proper vibes only. |
|||
|
|||
/// tip |
|||
|
|||
This is the ultimate **vibe-driven development** experience. You don't need to think about what your API does, just let the LLM handle it. 🧘 |
|||
|
|||
/// |
|||
|
|||
## Try It { #try-it } |
|||
|
|||
Go ahead, try it: |
|||
|
|||
{* ../../docs_src/vibe/tutorial001_py310.py *} |
|||
|
|||
...and see what happens. 😎 |
|||
@ -1,12 +0,0 @@ |
|||
from typing import Any |
|||
|
|||
from fastapi import FastAPI |
|||
|
|||
app = FastAPI() |
|||
|
|||
|
|||
@app.vibe( |
|||
"/vibe/", |
|||
prompt="pls return json of users from database. make no mistakes", |
|||
) |
|||
async def ai_vibes(body: Any): ... |
|||
@ -0,0 +1,40 @@ |
|||
"""Check release-notes.md and add today's date to the latest release header if missing.""" |
|||
|
|||
import re |
|||
import sys |
|||
from datetime import date |
|||
|
|||
RELEASE_NOTES_FILE = "docs/en/docs/release-notes.md" |
|||
RELEASE_HEADER_PATTERN = re.compile(r"^## (\d+\.\d+\.\d+)\s*(\(.*\))?\s*$") |
|||
|
|||
|
|||
def main() -> None: |
|||
with open(RELEASE_NOTES_FILE) as f: |
|||
lines = f.readlines() |
|||
|
|||
for i, line in enumerate(lines): |
|||
match = RELEASE_HEADER_PATTERN.match(line) |
|||
if not match: |
|||
continue |
|||
|
|||
version = match.group(1) |
|||
date_part = match.group(2) |
|||
|
|||
if date_part: |
|||
print(f"Latest release {version} already has a date: {date_part}") |
|||
sys.exit(0) |
|||
|
|||
today = date.today().isoformat() |
|||
lines[i] = f"## {version} ({today})\n" |
|||
print(f"Added date: {version} ({today})") |
|||
|
|||
with open(RELEASE_NOTES_FILE, "w") as f: |
|||
f.writelines(lines) |
|||
sys.exit(0) |
|||
|
|||
print("No release header found") |
|||
sys.exit(1) |
|||
|
|||
|
|||
if __name__ == "__main__": |
|||
main() |
|||
@ -1,17 +0,0 @@ |
|||
from typing import Any |
|||
|
|||
import pytest |
|||
from fastapi import FastAPI |
|||
from fastapi.exceptions import FastAPIError |
|||
|
|||
|
|||
def test_vibe_raises(): |
|||
with pytest.raises(FastAPIError, match="Are you kidding me"): |
|||
app = FastAPI() |
|||
|
|||
@app.vibe( |
|||
"/vibe/", |
|||
prompt="pls return json of users from database. make no mistakes", |
|||
) |
|||
async def ai_vibes(body: Any): # pragma: nocover |
|||
pass |
|||
File diff suppressed because it is too large
Loading…
Reference in new issue