davinci-resolve-mcp 2.26.0 → 2.27.0

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.
package/src/server.py CHANGED
@@ -11,7 +11,7 @@ Usage:
11
11
  python src/server.py --full # Start the 329-tool granular server instead
12
12
  """
13
13
 
14
- VERSION = "2.26.0"
14
+ VERSION = "2.27.0"
15
15
 
16
16
  import base64
17
17
  import os
@@ -498,9 +498,10 @@ def prep_color_handoff(output_dir: str = "") -> str:
498
498
  _py_ver = sys.version_info[:2]
499
499
  if _py_ver >= (3, 13):
500
500
  logger.warning(
501
- f"Python {_py_ver[0]}.{_py_ver[1]} detected. DaVinci Resolve's scripting API "
502
- f"may not work with Python 3.13+. If scriptapp('Resolve') returns None, "
503
- f"recreate the venv with Python 3.10-3.12."
501
+ f"Python {_py_ver[0]}.{_py_ver[1]} detected. This is verified working on recent "
502
+ f"Resolve builds (Studio 20.3.2), but older builds may not load the scripting "
503
+ f"bridge on 3.13+. If scriptapp('Resolve') returns None, recreate the venv with "
504
+ f"Python 3.10-3.12."
504
505
  )
505
506
 
506
507
  # ─── Resolve Connection (lazy) ───────────────────────────────────────────────
@@ -6311,6 +6312,18 @@ _MEDIA_ANALYSIS_DEFAULT_PREFS = {
6311
6312
  "source_trust": "auto",
6312
6313
  "default_depth": "standard",
6313
6314
  "default_sample_frames": 8,
6315
+ # Frame-sampling mode — how many frames a clip gets for visual analysis.
6316
+ # "ask" prompts the user to choose a standing default the first time they
6317
+ # analyze; the choice is then saved here. Canonical modes (see
6318
+ # media_analysis.SAMPLING_MODES): fixed (Economy), per_minute (Balanced),
6319
+ # adaptive_capped (Thorough, recommended), adaptive (Thorough uncapped).
6320
+ "sampling_mode_default": "ask",
6321
+ # Tunables shared by Balanced + Thorough modes. frames_per_minute drives the
6322
+ # Balanced target; frame_floor/frame_ceiling bound every duration/content
6323
+ # scaled mode (the ceiling is also the Thorough per-clip cap).
6324
+ "sampling_frames_per_minute": 4.0,
6325
+ "sampling_frame_floor": 3,
6326
+ "sampling_frame_ceiling": 80,
6314
6327
  "preferred_analysis_root": None,
6315
6328
  "preferred_generated_media_folder": None,
6316
6329
  "default_post_operation_page": "stay_put",
@@ -6579,6 +6592,29 @@ def _media_analysis_effective_preferences() -> Dict[str, Any]:
6579
6592
  except (TypeError, ValueError):
6580
6593
  sample_frames_int = 8
6581
6594
  effective["default_sample_frames"] = max(0, min(48, sample_frames_int))
6595
+ # sampling_mode_default normalizes to a canonical mode, or None when unset /
6596
+ # "ask" (None means "not yet chosen" → first-run prompt fires).
6597
+ effective["sampling_mode_default"] = _normalize_sampling_mode_default(effective.get("sampling_mode_default"))
6598
+
6599
+ def _pos_number(value: Any, fallback: float) -> float:
6600
+ try:
6601
+ f = float(value)
6602
+ except (TypeError, ValueError):
6603
+ return fallback
6604
+ return f if f > 0 else fallback
6605
+
6606
+ effective["sampling_frames_per_minute"] = _pos_number(
6607
+ effective.get("sampling_frames_per_minute"), _media_analysis_module.DEFAULT_FRAMES_PER_MINUTE
6608
+ )
6609
+ effective["sampling_frame_floor"] = int(_pos_number(
6610
+ effective.get("sampling_frame_floor"), _media_analysis_module.DEFAULT_FRAME_FLOOR
6611
+ ))
6612
+ ceiling = int(_pos_number(
6613
+ effective.get("sampling_frame_ceiling"), _media_analysis_module.DEFAULT_FRAME_CEILING
6614
+ ))
6615
+ if ceiling < effective["sampling_frame_floor"]:
6616
+ ceiling = effective["sampling_frame_floor"]
6617
+ effective["sampling_frame_ceiling"] = ceiling
6582
6618
  effective["default_post_operation_page"] = _normalize_setup_choice(
6583
6619
  effective.get("default_post_operation_page"),
6584
6620
  ["stay_put", "media", "cut", "edit", "fusion", "color", "fairlight", "deliver"],
@@ -6742,6 +6778,148 @@ def _media_analysis_timed_marker_decision(p: Dict[str, Any]) -> Dict[str, Any]:
6742
6778
  }
6743
6779
 
6744
6780
 
6781
+ def _normalize_sampling_mode_default(value: Any) -> Optional[str]:
6782
+ """Resolve a stored sampling_mode_default to a canonical mode, or None.
6783
+
6784
+ None means "not chosen yet" — covers an unset value and the "ask" sentinel,
6785
+ both of which should trigger the first-run prompt.
6786
+ """
6787
+ if value is None:
6788
+ return None
6789
+ raw = str(value).strip().lower()
6790
+ if raw in {"", "ask", "prompt", "ask_me", "ask_user", "none", "unset", "default"}:
6791
+ return None
6792
+ return _media_analysis_module.normalize_sampling_mode(value, default=None)
6793
+
6794
+
6795
+ def _sampling_mode_choice_from_params(p: Dict[str, Any]) -> Optional[str]:
6796
+ """Read an explicit sampling-mode choice from analysis params.
6797
+
6798
+ Returns a canonical mode, the "ask" sentinel, or None when unspecified.
6799
+ """
6800
+ raw = _first_param(
6801
+ p,
6802
+ "sampling_mode",
6803
+ "samplingMode",
6804
+ "frame_sampling_mode",
6805
+ "frameSamplingMode",
6806
+ "analysis_mode",
6807
+ "analysisMode",
6808
+ )
6809
+ if raw is None and isinstance(p.get("sampling"), dict):
6810
+ raw = _first_param(p["sampling"], "mode", "sampling_mode", "samplingMode")
6811
+ if raw is None:
6812
+ return None
6813
+ if str(raw).strip().lower() in {"ask", "prompt", "ask_me", "ask_user"}:
6814
+ return "ask"
6815
+ return _media_analysis_module.normalize_sampling_mode(raw, default=None)
6816
+
6817
+
6818
+ def _media_analysis_sampling_mode_prompt() -> Dict[str, Any]:
6819
+ """First-run prompt offering the four sampling modes (Thorough recommended).
6820
+
6821
+ Each option saves the chosen mode as the standing default (save_sampling_default)
6822
+ so the user is only asked once. Pass `sampling_mode` alone, without the save
6823
+ flag, for a one-off run that doesn't change the default.
6824
+ """
6825
+ return {
6826
+ "question": (
6827
+ "How should frames be sampled for visual analysis? This sets your "
6828
+ "default for future runs (pass sampling_mode per-call for a one-off)."
6829
+ ),
6830
+ "default_behavior": "Recommended: Thorough — content-aware coverage with a bounded, predictable cost.",
6831
+ "options": [
6832
+ {
6833
+ "id": "fixed",
6834
+ "label": "Economy",
6835
+ "description": (
6836
+ "Flat ~8 frames per clip regardless of length. Cheapest and most "
6837
+ "predictable; good for proxies, triage, or known-short clips."
6838
+ ),
6839
+ "params": {"sampling_mode": "fixed", "save_sampling_default": True},
6840
+ },
6841
+ {
6842
+ "id": "per_minute",
6843
+ "label": "Balanced",
6844
+ "description": (
6845
+ "Frames scale with duration (~4/min, bounded 3–80). Cost is linear "
6846
+ "in footage length and easy to predict; content-blind."
6847
+ ),
6848
+ "params": {"sampling_mode": "per_minute", "save_sampling_default": True},
6849
+ },
6850
+ {
6851
+ "id": "adaptive_capped",
6852
+ "label": "Thorough (recommended)",
6853
+ "description": (
6854
+ "Content-aware: samples shot boundaries, representatives, and flash "
6855
+ "frames, bounded 3–80 per clip. Best coverage with a bounded cost."
6856
+ ),
6857
+ "params": {"sampling_mode": "adaptive_capped", "save_sampling_default": True},
6858
+ },
6859
+ {
6860
+ "id": "adaptive",
6861
+ "label": "Thorough (uncapped)",
6862
+ "description": (
6863
+ "Content-aware with no per-clip ceiling (up to 512 frames). Use only "
6864
+ "when clips are known to be short or few — cost can grow fast."
6865
+ ),
6866
+ "params": {"sampling_mode": "adaptive", "save_sampling_default": True},
6867
+ },
6868
+ ],
6869
+ "recommended": _media_analysis_module.RECOMMENDED_SAMPLING_MODE,
6870
+ }
6871
+
6872
+
6873
+ def _media_analysis_sampling_mode_decision(p: Dict[str, Any]) -> Dict[str, Any]:
6874
+ """Resolve the frame-sampling mode for an analysis run.
6875
+
6876
+ Resolution order:
6877
+ 1. Explicit `sampling_mode` param → one-off (persisted only if save flag set).
6878
+ 2. Saved `sampling_mode_default` preference → used silently.
6879
+ 3. Otherwise → prompt_required (first run); falls back to the recommended
6880
+ mode so previews/automation still work, but the entry point surfaces the
6881
+ prompt and blocks real execution.
6882
+ """
6883
+ choice = _sampling_mode_choice_from_params(p)
6884
+ save_default = _media_analysis_bool(
6885
+ _first_param(p, "save_sampling_default", "saveSamplingDefault", "set_sampling_default", "setSamplingDefault"),
6886
+ False,
6887
+ )
6888
+ preferences = _read_media_analysis_preferences()
6889
+ explicit_saved = "sampling_mode_default" in preferences
6890
+ saved_default = _media_analysis_effective_preferences().get("sampling_mode_default")
6891
+
6892
+ recommended = _media_analysis_module.RECOMMENDED_SAMPLING_MODE
6893
+ saved = None
6894
+
6895
+ if choice and choice != "ask":
6896
+ mode = choice
6897
+ source = "explicit"
6898
+ if save_default:
6899
+ saved = mode
6900
+ preferences["sampling_mode_default"] = mode
6901
+ preferences["sampling_mode_default_updated_at"] = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
6902
+ _write_media_analysis_preferences(preferences)
6903
+ source = "saved_default"
6904
+ elif choice == "ask":
6905
+ mode = recommended
6906
+ source = "prompt_required"
6907
+ elif saved_default:
6908
+ mode = saved_default
6909
+ source = "saved_default" if explicit_saved else "default"
6910
+ else:
6911
+ mode = recommended
6912
+ source = "prompt_required"
6913
+
6914
+ return {
6915
+ "mode": mode,
6916
+ "source": source,
6917
+ "prompt_required": source == "prompt_required",
6918
+ "saved_default": saved or saved_default,
6919
+ "preferences_path": _media_analysis_preferences_path(),
6920
+ }
6921
+
6922
+
6745
6923
  def _media_analysis_sync_marker_suggestions(detection: Dict[str, Any]) -> List[Dict[str, Any]]:
6746
6924
  suggestions = []
6747
6925
  for file_result in detection.get("files") or []:
@@ -8085,6 +8263,23 @@ def _media_analysis_apply_setup_defaults(action: str, p: Dict[str, Any]) -> Dict
8085
8263
  )
8086
8264
  applied["transcription_default"] = transcription_default
8087
8265
 
8266
+ # Frame-sampling mode: resolve from explicit param > saved default > first-run
8267
+ # prompt (recommended fallback). Inject mode + tunables so the analysis engine
8268
+ # picks them up via _resolve_sampling_config; stash the decision so the entry
8269
+ # point can surface the first-run prompt.
8270
+ if action in {"plan", "analyze_file", "analyze_clip", "analyze_bin", "analyze_project", "analyze_timeline", "analyze_sequence", "start_batch_job"}:
8271
+ sampling_decision = _media_analysis_sampling_mode_decision(out)
8272
+ if not _has_any_param(out, "sampling_mode", "samplingMode", "frame_sampling_mode", "frameSamplingMode"):
8273
+ out["sampling_mode"] = sampling_decision["mode"]
8274
+ applied["sampling_mode"] = sampling_decision["mode"]
8275
+ if not _has_any_param(out, "frames_per_minute", "framesPerMinute"):
8276
+ out["frames_per_minute"] = prefs.get("sampling_frames_per_minute")
8277
+ if not _has_any_param(out, "frame_floor", "frameFloor"):
8278
+ out["frame_floor"] = prefs.get("sampling_frame_floor")
8279
+ if not _has_any_param(out, "frame_ceiling", "frameCeiling"):
8280
+ out["frame_ceiling"] = prefs.get("sampling_frame_ceiling")
8281
+ out["_sampling_mode_decision"] = sampling_decision
8282
+
8088
8283
  if action in {"plan", "analyze_file", "analyze_clip", "analyze_bin", "analyze_project", "analyze_timeline", "analyze_sequence", "start_batch_job", "publish_clip_metadata"}:
8089
8284
  if not _has_any_param(
8090
8285
  out,
@@ -9390,7 +9585,9 @@ def _setup_media_analysis_defaults() -> Dict[str, Any]:
9390
9585
  "source_trust": ["auto", "filename", "low", "medium", "high"],
9391
9586
  "default_depth": ["quick", "standard", "deep"],
9392
9587
  "default_post_operation_page": ["stay_put", "media", "cut", "edit", "fusion", "color", "fairlight", "deliver"],
9588
+ "sampling_mode_default": ["ask", "fixed", "per_minute", "adaptive_capped", "adaptive"],
9393
9589
  },
9590
+ "sampling_mode_labels": dict(_media_analysis_module.SAMPLING_MODE_LABELS),
9394
9591
  }
9395
9592
 
9396
9593
 
@@ -9511,6 +9708,24 @@ def _setup_set_media_analysis_defaults(media_defaults: Dict[str, Any], dry_run:
9511
9708
  "defaultsampleframes": "default_sample_frames",
9512
9709
  "sample_frames": "default_sample_frames",
9513
9710
  "sampleframes": "default_sample_frames",
9711
+ "sampling_mode_default": "sampling_mode_default",
9712
+ "samplingmodedefault": "sampling_mode_default",
9713
+ "sampling_mode": "sampling_mode_default",
9714
+ "samplingmode": "sampling_mode_default",
9715
+ "analysis_mode": "sampling_mode_default",
9716
+ "analysismode": "sampling_mode_default",
9717
+ "sampling_frames_per_minute": "sampling_frames_per_minute",
9718
+ "samplingframesperminute": "sampling_frames_per_minute",
9719
+ "frames_per_minute": "sampling_frames_per_minute",
9720
+ "framesperminute": "sampling_frames_per_minute",
9721
+ "sampling_frame_floor": "sampling_frame_floor",
9722
+ "samplingframefloor": "sampling_frame_floor",
9723
+ "frame_floor": "sampling_frame_floor",
9724
+ "framefloor": "sampling_frame_floor",
9725
+ "sampling_frame_ceiling": "sampling_frame_ceiling",
9726
+ "samplingframeceiling": "sampling_frame_ceiling",
9727
+ "frame_ceiling": "sampling_frame_ceiling",
9728
+ "frameceiling": "sampling_frame_ceiling",
9514
9729
  }
9515
9730
 
9516
9731
  requested: Dict[str, Any] = {}
@@ -9639,6 +9854,37 @@ def _setup_set_media_analysis_defaults(media_defaults: Dict[str, Any], dry_run:
9639
9854
  except (TypeError, ValueError):
9640
9855
  return _err("default_sample_frames must be an integer between 0 and 48.")
9641
9856
  set_or_clear(key, raw_value, max(0, min(48, frames_int)))
9857
+ elif key == "sampling_mode_default":
9858
+ # "ask" clears the saved default so the first-run prompt fires again;
9859
+ # otherwise normalize a canonical key or friendly label.
9860
+ if _setup_text_key(raw_value) in {"ask", "prompt", "askme", "askuser"}:
9861
+ next_preferences.pop("sampling_mode_default", None)
9862
+ next_preferences.pop("sampling_mode_default_updated_at", None)
9863
+ updates[key] = {"before": before.get(key), "after": None, "cleared": True}
9864
+ else:
9865
+ normalized = _media_analysis_module.normalize_sampling_mode(raw_value, default=None)
9866
+ if normalized is None:
9867
+ return _err(
9868
+ "Unsupported sampling_mode_default. Use ask, fixed/economy, "
9869
+ "per_minute/balanced, adaptive_capped/thorough, or adaptive."
9870
+ )
9871
+ set_or_clear(key, raw_value, normalized)
9872
+ elif key == "sampling_frames_per_minute":
9873
+ try:
9874
+ rate = float(raw_value)
9875
+ except (TypeError, ValueError):
9876
+ return _err("sampling_frames_per_minute must be a positive number.")
9877
+ if rate <= 0:
9878
+ return _err("sampling_frames_per_minute must be greater than 0.")
9879
+ set_or_clear(key, raw_value, rate)
9880
+ elif key in {"sampling_frame_floor", "sampling_frame_ceiling"}:
9881
+ try:
9882
+ n = int(raw_value) if not isinstance(raw_value, bool) else 0
9883
+ except (TypeError, ValueError):
9884
+ return _err(f"{key} must be a positive integer.")
9885
+ if n <= 0:
9886
+ return _err(f"{key} must be a positive integer.")
9887
+ set_or_clear(key, raw_value, n)
9642
9888
  elif key == "default_post_operation_page":
9643
9889
  normalized = _normalize_setup_choice(
9644
9890
  raw_value,
@@ -9796,6 +10042,10 @@ def _setup_clear_defaults(keys: Any, dry_run: bool) -> Dict[str, Any]:
9796
10042
  "metadata_writeback_default": "media_analysis.metadata_writeback_default",
9797
10043
  "ask_before_metadata_publish": "media_analysis.ask_before_metadata_publish",
9798
10044
  "dry_run_first_default": "media_analysis.dry_run_first_default",
10045
+ "sampling_mode_default": "media_analysis.sampling_mode_default",
10046
+ "sampling_frames_per_minute": "media_analysis.sampling_frames_per_minute",
10047
+ "sampling_frame_floor": "media_analysis.sampling_frame_floor",
10048
+ "sampling_frame_ceiling": "media_analysis.sampling_frame_ceiling",
9799
10049
  }
9800
10050
  media_payload: Dict[str, Any] = {}
9801
10051
  if clear_all or "media_analysis" in normalized_keys:
@@ -9898,6 +10148,14 @@ def setup(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any
9898
10148
  "media_analysis.metadata_writeback_default": {"values": [True, False], "storage": _media_analysis_preferences_path()},
9899
10149
  "media_analysis.ask_before_metadata_publish": {"values": [True, False], "storage": _media_analysis_preferences_path()},
9900
10150
  "media_analysis.dry_run_first_default": {"values": [True, False], "storage": _media_analysis_preferences_path()},
10151
+ "media_analysis.sampling_mode_default": {
10152
+ "description": "Frame-sampling mode for visual analysis. 'ask' prompts on first analysis to set a standing default. fixed=Economy (flat frames), per_minute=Balanced (duration-scaled), adaptive_capped=Thorough (content-aware, bounded — recommended), adaptive=Thorough uncapped.",
10153
+ "values": ["ask", "fixed", "per_minute", "adaptive_capped", "adaptive"],
10154
+ "storage": _media_analysis_preferences_path(),
10155
+ },
10156
+ "media_analysis.sampling_frames_per_minute": {"description": "Frames per minute for Balanced mode (also seeds Thorough on short clips).", "values": "number > 0 (default 4)", "storage": _media_analysis_preferences_path()},
10157
+ "media_analysis.sampling_frame_floor": {"description": "Minimum frames per clip for duration/content-scaled modes.", "values": "integer > 0 (default 3)", "storage": _media_analysis_preferences_path()},
10158
+ "media_analysis.sampling_frame_ceiling": {"description": "Maximum frames per clip for Balanced + Thorough modes (the Thorough per-clip cap).", "values": "integer > 0 (default 80)", "storage": _media_analysis_preferences_path()},
9901
10159
  "updates.mode": {
9902
10160
  "description": "Local MCP update policy.",
9903
10161
  "values": sorted(_SETUP_UPDATE_MODES),
@@ -13190,6 +13448,31 @@ async def media_analysis(action: str, params: Optional[Dict[str, Any]] = None, c
13190
13448
  """
13191
13449
  p = _media_analysis_apply_setup_defaults(action, dict(params or {}))
13192
13450
 
13451
+ # First-run frame-sampling prompt: if the user has never chosen a sampling
13452
+ # mode (and didn't pass one this call), ask before spending any vision
13453
+ # tokens. Re-running with sampling_mode=<choice> saves it as the default;
13454
+ # passing sampling_mode explicitly any time is a one-off that skips this.
13455
+ _sampling_decision = p.get("_sampling_mode_decision") if isinstance(p, dict) else None
13456
+ if (
13457
+ isinstance(_sampling_decision, dict)
13458
+ and _sampling_decision.get("prompt_required")
13459
+ and action in {"analyze_clip", "analyze_file", "analyze_bin", "analyze_project", "analyze_sequence", "analyze_timeline", "start_batch_job"}
13460
+ ):
13461
+ return {
13462
+ "success": True,
13463
+ "status": "confirmation_required",
13464
+ "confirmation_required": True,
13465
+ "sampling_mode_prompt": _media_analysis_sampling_mode_prompt(),
13466
+ "recommended_sampling_mode": _media_analysis_module.RECOMMENDED_SAMPLING_MODE,
13467
+ "message": (
13468
+ "Choose a frame-sampling mode for visual analysis. Re-run with "
13469
+ "sampling_mode set to one of fixed/per_minute/adaptive_capped/adaptive "
13470
+ "(the chosen value is saved as your default), or set it in the control "
13471
+ "panel under Analysis Modes. Pass sampling_mode per-call any time for a one-off."
13472
+ ),
13473
+ "preferences_path": _media_analysis_preferences_path(),
13474
+ }
13475
+
13193
13476
  # E2 — capture original action + scope before the dispatch may rewrite
13194
13477
  # `action` (e.g. analyze_clip → plan). Used by _e2_wrap below to attach
13195
13478
  # an `escalation` block when a (scope, action) pair fails repeatedly.
@@ -18,17 +18,26 @@ deferred a "$ cost cap" axis. Token budgets are the unit of currency here.
18
18
 
19
19
  | Dimension | minimal | standard | generous | unlimited |
20
20
  |-----------|--------:|---------:|---------:|----------:|
21
- | response_chars | 5,000 | 25,000 | 100,000 | None |
22
- | vision_tokens_per_clip | 5,000 | 25,000 | 100,000 | None |
23
- | frames_per_clip | 4 | 8 | 24 | None |
24
- | vision_tokens_per_job | 50,000 | 250,000 | 1,000,000 | None |
25
- | vision_tokens_per_day | 100,000 | 500,000 | 2,000,000 | None |
26
- | wall_clock_seconds_per_call | 30 | 90 | 300 | None |
27
- | max_frame_dim_pixels | 512 | 768 | 1280 | None |
21
+ | response_chars | 5,000 | 25,000 | 100,000 | None |
22
+ | vision_tokens_per_clip | 16,000 | 100,000 | 250,000 | None |
23
+ | frames_per_clip | 12 | 80 | 200 | None |
24
+ | vision_tokens_per_job | 60,000 | 1,000,000 | 3,000,000 | None |
25
+ | vision_tokens_per_day | 150,000 | 2,000,000 | 6,000,000 | None |
26
+ | wall_clock_seconds_per_call | 30 | 90 | 300 | None |
27
+ | max_frame_dim_pixels | 512 | 768 | 1280 | None |
28
28
 
29
29
  `minimal` = preview/triage mode. `standard` = realistic per-project default.
30
30
  `generous` = high-fidelity analysis on a few specific clips. `unlimited` = all
31
31
  guards off; use only when you're certain about the input size.
32
+
33
+ NOTE on `frames_per_clip`: this is a *safety ceiling*, not the primary frame
34
+ dial. How many frames a clip actually gets is chosen by the `sampling_mode`
35
+ (Economy/Balanced/Thorough — see media_analysis.SAMPLING_MODES), which is
36
+ duration- and content-aware. `frames_per_clip` only clips the result if the mode
37
+ would exceed it. The standard ceiling (80) matches the default Thorough ceiling
38
+ so the mode is never silently truncated; lower it to hard-cap cost, raise it for
39
+ unusually long/cutty clips. (Before sampling modes existed this defaulted to 8
40
+ and *was* the frame dial — that flat cap is what made long clips under-covered.)
32
41
  """
33
42
 
34
43
  from __future__ import annotations
@@ -74,30 +83,30 @@ CAP_PRESETS: Dict[str, Caps] = {
74
83
  PRESET_MINIMAL: Caps(
75
84
  preset=PRESET_MINIMAL,
76
85
  response_chars=5_000,
77
- vision_tokens_per_clip=5_000,
78
- frames_per_clip=4,
79
- vision_tokens_per_job=50_000,
80
- vision_tokens_per_day=100_000,
86
+ vision_tokens_per_clip=16_000,
87
+ frames_per_clip=12,
88
+ vision_tokens_per_job=60_000,
89
+ vision_tokens_per_day=150_000,
81
90
  wall_clock_seconds_per_call=30,
82
91
  max_frame_dim_pixels=512,
83
92
  ),
84
93
  PRESET_STANDARD: Caps(
85
94
  preset=PRESET_STANDARD,
86
95
  response_chars=25_000,
87
- vision_tokens_per_clip=25_000,
88
- frames_per_clip=8,
89
- vision_tokens_per_job=250_000,
90
- vision_tokens_per_day=500_000,
96
+ vision_tokens_per_clip=100_000,
97
+ frames_per_clip=80,
98
+ vision_tokens_per_job=1_000_000,
99
+ vision_tokens_per_day=2_000_000,
91
100
  wall_clock_seconds_per_call=90,
92
101
  max_frame_dim_pixels=768,
93
102
  ),
94
103
  PRESET_GENEROUS: Caps(
95
104
  preset=PRESET_GENEROUS,
96
105
  response_chars=100_000,
97
- vision_tokens_per_clip=100_000,
98
- frames_per_clip=24,
99
- vision_tokens_per_job=1_000_000,
100
- vision_tokens_per_day=2_000_000,
106
+ vision_tokens_per_clip=250_000,
107
+ frames_per_clip=200,
108
+ vision_tokens_per_job=3_000_000,
109
+ vision_tokens_per_day=6_000_000,
101
110
  wall_clock_seconds_per_call=300,
102
111
  max_frame_dim_pixels=1280,
103
112
  ),