Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ MEDIA_STUDIO_APP_ENV=development
# Optional: leave these blank for the startup scripts to derive them from MEDIA_STUDIO_API_HOST and MEDIA_STUDIO_API_PORT.
NEXT_PUBLIC_MEDIA_STUDIO_CONTROL_API_BASE_URL=
MEDIA_STUDIO_CONTROL_API_BASE_URL=
# Optional: set to 1 to show the experimental Graph Studio Media Assistant.
NEXT_PUBLIC_MEDIA_STUDIO_ASSISTANT_DEBUG=
# Internal web->API token. Keep the same value for both the Next app and FastAPI.
# Generate a unique random value for your machine or deployment.
MEDIA_STUDIO_CONTROL_API_TOKEN=replace_with_a_unique_control_token
Expand All @@ -13,6 +15,9 @@ MEDIA_STUDIO_ADMIN_USERNAME=
MEDIA_STUDIO_ADMIN_PASSWORD=
# Optional: allow access from private LAN / TailScale addresses without browser auth.
MEDIA_STUDIO_ALLOW_PRIVATE_NETWORK_ACCESS=false
# Optional extra Next.js dev origins for private LAN / TailScale browser access.
# Usually not needed when MEDIA_STUDIO_ALLOW_PRIVATE_NETWORK_ACCESS=true.
MEDIA_STUDIO_ALLOWED_DEV_ORIGINS=

# API
MEDIA_STUDIO_API_HOST=127.0.0.1
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ docs/API_SECURITY_FULL_REPORT.md
docs/review-remediation-plan.md
logs/
tmp/
tmp-*
temp/
output/
outputs/
Expand All @@ -79,4 +80,3 @@ apps/api/*.egg-info/
apps/api/media_studio.db
apps/web/.turbo/
apps/web/tsconfig.tsbuildinfo
tmp-graph-studio.png
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,14 @@ If the default ports are busy, Studio automatically chooses the next open API an
npm run start:studio -- --api-port 8010 --web-port 3010
```

At the end of onboarding, any displayed `127.0.0.1:3000` URL is only the configured/default URL. If that port is busy, the launcher prints and opens the actual temporary Studio URL it selected.

The standalone developer commands use the same port-safety behavior: `npm run dev:api`, `npm run start:api`, `npm run dev:web`, and `npm run start:web` auto-select a temporary open port unless you pass an explicit `--port`.

### Release Flags

Graph Studio hides the experimental Media Assistant chat button by default. To expose it for internal debugging, set `NEXT_PUBLIC_MEDIA_STUDIO_ASSISTANT_DEBUG=1` in `.env` and restart the web app so Next.js picks up the client-side flag.

## Cool Features

- **Create Revision** restores an old asset back into Studio with the original prompt, model, settings, and reference media.
Expand Down
4 changes: 3 additions & 1 deletion START_HERE.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,14 @@ npm run dev

That runs the app in development mode and can show dev-only UI such as the Next.js badge or overlay.

Then open:
Then open the URL printed by the launcher. On a default free-port launch, those routes are:

- `http://127.0.0.1:3000/studio`
- `http://127.0.0.1:3000/settings`
- `http://127.0.0.1:3000/pricing`

If port `3000` is already in use, the launcher automatically picks another web port and prints the actual Studio URL for that launch.

## What to look at first

- `/setup`
Expand Down
1 change: 1 addition & 0 deletions apps/api/app/assistant/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Media Studio Creative Assistant backend package."""
41 changes: 41 additions & 0 deletions apps/api/app/assistant/cancellation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from __future__ import annotations

from contextlib import contextmanager
from threading import Event, Lock
from typing import Iterator


class AssistantRequestCancelled(Exception):
"""Raised when an in-flight assistant provider turn is cancelled."""


_lock = Lock()
_events: dict[str, Event] = {}


@contextmanager
def track_session(session_id: str) -> Iterator[Event]:
with _lock:
event = _events.get(session_id)
if event is None or event.is_set():
event = Event()
_events[session_id] = event
try:
yield event
finally:
with _lock:
if _events.get(session_id) is event:
_events.pop(session_id, None)


def cancel_session(session_id: str) -> bool:
with _lock:
event = _events.get(session_id)
if not event:
return False
event.set()
return True


def is_cancelled(event: Event | None) -> bool:
return bool(event and event.is_set())
Loading