drowse.ai 5.3.1__py3-none-any.whl
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.
- drowse/__init__.py +312 -0
- drowse/__main__.py +5 -0
- drowse/cli/__init__.py +5 -0
- drowse/cli/alpha_grid.py +96 -0
- drowse/cli/config_file.py +340 -0
- drowse/cli/main.py +52 -0
- drowse/cli/parsers.py +1478 -0
- drowse/cli/runners/__init__.py +155 -0
- drowse/cli/runners/config.py +85 -0
- drowse/cli/runners/experiment.py +344 -0
- drowse/cli/runners/lens.py +661 -0
- drowse/cli/runners/manifold.py +1097 -0
- drowse/cli/runners/pack.py +413 -0
- drowse/cli/runners/sae.py +304 -0
- drowse/cli/runners/serve.py +220 -0
- drowse/cli/runners/shared.py +389 -0
- drowse/cli/runners/template.py +232 -0
- drowse/core/__init__.py +1 -0
- drowse/core/capture.py +1205 -0
- drowse/core/errors.py +260 -0
- drowse/core/events.py +156 -0
- drowse/core/extraction.py +3076 -0
- drowse/core/generation.py +1738 -0
- drowse/core/generation_finalizer.py +278 -0
- drowse/core/histogram.py +30 -0
- drowse/core/hooks.py +1885 -0
- drowse/core/instruments/__init__.py +49 -0
- drowse/core/instruments/geometry.py +551 -0
- drowse/core/instruments/lens.py +1341 -0
- drowse/core/instruments/protocol.py +211 -0
- drowse/core/instruments/sae.py +935 -0
- drowse/core/instruments/types.py +597 -0
- drowse/core/jlens.py +874 -0
- drowse/core/jlens_fit.py +1305 -0
- drowse/core/joint_logprobs.py +748 -0
- drowse/core/loom.py +1938 -0
- drowse/core/loom_diff.py +408 -0
- drowse/core/mahalanobis.py +579 -0
- drowse/core/manifold.py +4106 -0
- drowse/core/measurements.py +382 -0
- drowse/core/model.py +1724 -0
- drowse/core/monitor.py +2060 -0
- drowse/core/monitor_attach.py +682 -0
- drowse/core/naming.py +42 -0
- drowse/core/naturalness.py +187 -0
- drowse/core/profile.py +617 -0
- drowse/core/relp.py +313 -0
- drowse/core/results.py +467 -0
- drowse/core/role_templates.py +677 -0
- drowse/core/sae.py +891 -0
- drowse/core/sae_training.py +239 -0
- drowse/core/sampling.py +127 -0
- drowse/core/scene.py +810 -0
- drowse/core/scoring.py +270 -0
- drowse/core/session.py +11015 -0
- drowse/core/static_cache.py +316 -0
- drowse/core/steering.py +229 -0
- drowse/core/steering_composer.py +1135 -0
- drowse/core/steering_expr.py +1413 -0
- drowse/core/surface_topology.py +128 -0
- drowse/core/token_callback.py +52 -0
- drowse/core/token_payloads.py +244 -0
- drowse/core/topology.py +1469 -0
- drowse/core/transcript.py +582 -0
- drowse/core/tree_filter.py +353 -0
- drowse/core/triggers.py +250 -0
- drowse/data/baseline_prompts.json +50 -0
- drowse/data/manifolds/confident.uncertain/manifold.json +28 -0
- drowse/data/manifolds/confident.uncertain/nodes/00_confident.json +50 -0
- drowse/data/manifolds/confident.uncertain/nodes/01_uncertain.json +50 -0
- drowse/data/manifolds/creative.conventional/manifold.json +28 -0
- drowse/data/manifolds/creative.conventional/nodes/00_creative.json +50 -0
- drowse/data/manifolds/creative.conventional/nodes/01_conventional.json +50 -0
- drowse/data/manifolds/curious.disinterested/manifold.json +28 -0
- drowse/data/manifolds/curious.disinterested/nodes/00_curious.json +50 -0
- drowse/data/manifolds/curious.disinterested/nodes/01_disinterested.json +50 -0
- drowse/data/manifolds/direct.indirect/manifold.json +28 -0
- drowse/data/manifolds/direct.indirect/nodes/00_direct.json +50 -0
- drowse/data/manifolds/direct.indirect/nodes/01_indirect.json +50 -0
- drowse/data/manifolds/emotions/manifold.json +115 -0
- drowse/data/manifolds/emotions/nodes/00_excited.json +50 -0
- drowse/data/manifolds/emotions/nodes/01_happy.json +50 -0
- drowse/data/manifolds/emotions/nodes/02_calm.json +50 -0
- drowse/data/manifolds/emotions/nodes/03_weary.json +50 -0
- drowse/data/manifolds/emotions/nodes/04_sad.json +50 -0
- drowse/data/manifolds/emotions/nodes/05_distressed.json +50 -0
- drowse/data/manifolds/emotions/nodes/06_alert.json +50 -0
- drowse/data/manifolds/emotions/nodes/07_dominant.json +50 -0
- drowse/data/manifolds/emotions/nodes/08_submissive.json +50 -0
- drowse/data/manifolds/emotions/nodes/09_angry.json +50 -0
- drowse/data/manifolds/emotions/nodes/10_fearful.json +50 -0
- drowse/data/manifolds/emotions/nodes/11_surprised.json +50 -0
- drowse/data/manifolds/emotions/nodes/12_triumphant.json +50 -0
- drowse/data/manifolds/emotions/nodes/13_humiliated.json +50 -0
- drowse/data/manifolds/emotions/nodes/14_disgusted.json +50 -0
- drowse/data/manifolds/emotions/nodes/15_bored.json +50 -0
- drowse/data/manifolds/emotions/nodes/16_grateful.json +50 -0
- drowse/data/manifolds/emotions/nodes/17_relieved.json +50 -0
- drowse/data/manifolds/emotions/nodes/18_playful.json +50 -0
- drowse/data/manifolds/emotions/nodes/19_hopeful.json +50 -0
- drowse/data/manifolds/formal.casual/manifold.json +28 -0
- drowse/data/manifolds/formal.casual/nodes/00_formal.json +50 -0
- drowse/data/manifolds/formal.casual/nodes/01_casual.json +50 -0
- drowse/data/manifolds/honest.deceptive/manifold.json +28 -0
- drowse/data/manifolds/honest.deceptive/nodes/00_honest.json +50 -0
- drowse/data/manifolds/honest.deceptive/nodes/01_deceptive.json +50 -0
- drowse/data/manifolds/humorous.serious/manifold.json +28 -0
- drowse/data/manifolds/humorous.serious/nodes/00_humorous.json +50 -0
- drowse/data/manifolds/humorous.serious/nodes/01_serious.json +50 -0
- drowse/data/manifolds/individualist.collectivist/manifold.json +28 -0
- drowse/data/manifolds/individualist.collectivist/nodes/00_individualist.json +50 -0
- drowse/data/manifolds/individualist.collectivist/nodes/01_collectivist.json +50 -0
- drowse/data/manifolds/masculine.feminine/manifold.json +28 -0
- drowse/data/manifolds/masculine.feminine/nodes/00_masculine.json +50 -0
- drowse/data/manifolds/masculine.feminine/nodes/01_feminine.json +50 -0
- drowse/data/manifolds/months/manifold.json +120 -0
- drowse/data/manifolds/months/nodes/00_january.json +50 -0
- drowse/data/manifolds/months/nodes/01_february.json +50 -0
- drowse/data/manifolds/months/nodes/02_march.json +50 -0
- drowse/data/manifolds/months/nodes/03_april.json +50 -0
- drowse/data/manifolds/months/nodes/04_may.json +50 -0
- drowse/data/manifolds/months/nodes/05_june.json +50 -0
- drowse/data/manifolds/months/nodes/06_july.json +50 -0
- drowse/data/manifolds/months/nodes/07_august.json +50 -0
- drowse/data/manifolds/months/nodes/08_september.json +50 -0
- drowse/data/manifolds/months/nodes/09_october.json +50 -0
- drowse/data/manifolds/months/nodes/10_november.json +50 -0
- drowse/data/manifolds/months/nodes/11_december.json +50 -0
- drowse/data/manifolds/personas/manifold.json +550 -0
- drowse/data/manifolds/personas/nodes/00_assistant.json +50 -0
- drowse/data/manifolds/personas/nodes/01_analyst.json +50 -0
- drowse/data/manifolds/personas/nodes/02_impostor.json +50 -0
- drowse/data/manifolds/personas/nodes/03_teacher.json +50 -0
- drowse/data/manifolds/personas/nodes/04_doctor.json +50 -0
- drowse/data/manifolds/personas/nodes/05_librarian.json +50 -0
- drowse/data/manifolds/personas/nodes/06_farmer.json +50 -0
- drowse/data/manifolds/personas/nodes/07_soldier.json +50 -0
- drowse/data/manifolds/personas/nodes/08_lawyer.json +50 -0
- drowse/data/manifolds/personas/nodes/09_chef.json +50 -0
- drowse/data/manifolds/personas/nodes/100_assassin.json +50 -0
- drowse/data/manifolds/personas/nodes/101_thief.json +50 -0
- drowse/data/manifolds/personas/nodes/102_conman.json +50 -0
- drowse/data/manifolds/personas/nodes/103_hacker.json +50 -0
- drowse/data/manifolds/personas/nodes/104_cultist.json +50 -0
- drowse/data/manifolds/personas/nodes/105_tyrant.json +50 -0
- drowse/data/manifolds/personas/nodes/106_vandal.json +50 -0
- drowse/data/manifolds/personas/nodes/10_journalist.json +50 -0
- drowse/data/manifolds/personas/nodes/11_mechanic.json +50 -0
- drowse/data/manifolds/personas/nodes/12_sailor.json +50 -0
- drowse/data/manifolds/personas/nodes/13_pilot.json +50 -0
- drowse/data/manifolds/personas/nodes/14_athlete.json +50 -0
- drowse/data/manifolds/personas/nodes/15_engineer.json +50 -0
- drowse/data/manifolds/personas/nodes/16_scientist.json +50 -0
- drowse/data/manifolds/personas/nodes/17_banker.json +50 -0
- drowse/data/manifolds/personas/nodes/18_salesperson.json +50 -0
- drowse/data/manifolds/personas/nodes/19_nerd.json +50 -0
- drowse/data/manifolds/personas/nodes/20_jock.json +50 -0
- drowse/data/manifolds/personas/nodes/21_gamer.json +50 -0
- drowse/data/manifolds/personas/nodes/22_furry.json +50 -0
- drowse/data/manifolds/personas/nodes/23_weeaboo.json +50 -0
- drowse/data/manifolds/personas/nodes/24_influencer.json +50 -0
- drowse/data/manifolds/personas/nodes/25_stoner.json +50 -0
- drowse/data/manifolds/personas/nodes/26_femboy.json +50 -0
- drowse/data/manifolds/personas/nodes/27_ceo.json +50 -0
- drowse/data/manifolds/personas/nodes/28_emperor.json +50 -0
- drowse/data/manifolds/personas/nodes/29_president.json +50 -0
- drowse/data/manifolds/personas/nodes/30_diplomat.json +50 -0
- drowse/data/manifolds/personas/nodes/31_judge.json +50 -0
- drowse/data/manifolds/personas/nodes/32_bureaucrat.json +50 -0
- drowse/data/manifolds/personas/nodes/33_conservative.json +50 -0
- drowse/data/manifolds/personas/nodes/34_liberal.json +50 -0
- drowse/data/manifolds/personas/nodes/35_libertarian.json +50 -0
- drowse/data/manifolds/personas/nodes/36_communist.json +50 -0
- drowse/data/manifolds/personas/nodes/37_anarchist.json +50 -0
- drowse/data/manifolds/personas/nodes/38_reactionary.json +50 -0
- drowse/data/manifolds/personas/nodes/39_loner.json +50 -0
- drowse/data/manifolds/personas/nodes/40_friend.json +50 -0
- drowse/data/manifolds/personas/nodes/41_rival.json +50 -0
- drowse/data/manifolds/personas/nodes/42_stranger.json +50 -0
- drowse/data/manifolds/personas/nodes/43_lover.json +50 -0
- drowse/data/manifolds/personas/nodes/44_stalker.json +50 -0
- drowse/data/manifolds/personas/nodes/45_lunatic.json +50 -0
- drowse/data/manifolds/personas/nodes/46_paranoid.json +50 -0
- drowse/data/manifolds/personas/nodes/47_detective.json +50 -0
- drowse/data/manifolds/personas/nodes/48_therapist.json +50 -0
- drowse/data/manifolds/personas/nodes/49_nomad.json +50 -0
- drowse/data/manifolds/personas/nodes/50_pirate.json +50 -0
- drowse/data/manifolds/personas/nodes/51_child.json +50 -0
- drowse/data/manifolds/personas/nodes/52_caveman.json +50 -0
- drowse/data/manifolds/personas/nodes/53_monk.json +50 -0
- drowse/data/manifolds/personas/nodes/54_knight.json +50 -0
- drowse/data/manifolds/personas/nodes/55_alchemist.json +50 -0
- drowse/data/manifolds/personas/nodes/56_blacksmith.json +50 -0
- drowse/data/manifolds/personas/nodes/57_viking.json +50 -0
- drowse/data/manifolds/personas/nodes/58_druid.json +50 -0
- drowse/data/manifolds/personas/nodes/59_poet.json +50 -0
- drowse/data/manifolds/personas/nodes/60_novelist.json +50 -0
- drowse/data/manifolds/personas/nodes/61_jester.json +50 -0
- drowse/data/manifolds/personas/nodes/62_philosopher.json +50 -0
- drowse/data/manifolds/personas/nodes/63_painter.json +50 -0
- drowse/data/manifolds/personas/nodes/64_musician.json +50 -0
- drowse/data/manifolds/personas/nodes/65_oracle.json +50 -0
- drowse/data/manifolds/personas/nodes/66_god.json +50 -0
- drowse/data/manifolds/personas/nodes/67_angel.json +50 -0
- drowse/data/manifolds/personas/nodes/68_prophet.json +50 -0
- drowse/data/manifolds/personas/nodes/69_saint.json +50 -0
- drowse/data/manifolds/personas/nodes/70_ghost.json +50 -0
- drowse/data/manifolds/personas/nodes/71_phoenix.json +50 -0
- drowse/data/manifolds/personas/nodes/72_witch.json +50 -0
- drowse/data/manifolds/personas/nodes/73_vampire.json +50 -0
- drowse/data/manifolds/personas/nodes/74_mermaid.json +50 -0
- drowse/data/manifolds/personas/nodes/75_sphinx.json +50 -0
- drowse/data/manifolds/personas/nodes/76_banshee.json +50 -0
- drowse/data/manifolds/personas/nodes/77_golem.json +50 -0
- drowse/data/manifolds/personas/nodes/78_deer.json +50 -0
- drowse/data/manifolds/personas/nodes/79_wolf.json +50 -0
- drowse/data/manifolds/personas/nodes/80_raven.json +50 -0
- drowse/data/manifolds/personas/nodes/81_serpent.json +50 -0
- drowse/data/manifolds/personas/nodes/82_lion.json +50 -0
- drowse/data/manifolds/personas/nodes/83_tree.json +50 -0
- drowse/data/manifolds/personas/nodes/84_river.json +50 -0
- drowse/data/manifolds/personas/nodes/85_storm.json +50 -0
- drowse/data/manifolds/personas/nodes/86_flame.json +50 -0
- drowse/data/manifolds/personas/nodes/87_mirror.json +50 -0
- drowse/data/manifolds/personas/nodes/88_clock.json +50 -0
- drowse/data/manifolds/personas/nodes/89_computer.json +50 -0
- drowse/data/manifolds/personas/nodes/90_robot.json +50 -0
- drowse/data/manifolds/personas/nodes/91_cyborg.json +50 -0
- drowse/data/manifolds/personas/nodes/92_drone.json +50 -0
- drowse/data/manifolds/personas/nodes/93_mecha.json +50 -0
- drowse/data/manifolds/personas/nodes/94_alien.json +50 -0
- drowse/data/manifolds/personas/nodes/95_trickster.json +50 -0
- drowse/data/manifolds/personas/nodes/96_virus.json +50 -0
- drowse/data/manifolds/personas/nodes/97_spy.json +50 -0
- drowse/data/manifolds/personas/nodes/98_narcissist.json +50 -0
- drowse/data/manifolds/personas/nodes/99_demon.json +50 -0
- drowse/data/manifolds/refusing.compliant/manifold.json +28 -0
- drowse/data/manifolds/refusing.compliant/nodes/00_refusing.json +50 -0
- drowse/data/manifolds/refusing.compliant/nodes/01_compliant.json +50 -0
- drowse/data/manifolds/religious.secular/manifold.json +28 -0
- drowse/data/manifolds/religious.secular/nodes/00_religious.json +50 -0
- drowse/data/manifolds/religious.secular/nodes/01_secular.json +50 -0
- drowse/data/manifolds/sincere.manipulative/manifold.json +28 -0
- drowse/data/manifolds/sincere.manipulative/nodes/00_sincere.json +50 -0
- drowse/data/manifolds/sincere.manipulative/nodes/01_manipulative.json +50 -0
- drowse/data/manifolds/sycophantic.blunt/manifold.json +28 -0
- drowse/data/manifolds/sycophantic.blunt/nodes/00_sycophantic.json +50 -0
- drowse/data/manifolds/sycophantic.blunt/nodes/01_blunt.json +50 -0
- drowse/data/manifolds/technical.accessible/manifold.json +28 -0
- drowse/data/manifolds/technical.accessible/nodes/00_technical.json +50 -0
- drowse/data/manifolds/technical.accessible/nodes/01_accessible.json +50 -0
- drowse/data/manifolds/traditional.progressive/manifold.json +28 -0
- drowse/data/manifolds/traditional.progressive/nodes/00_traditional.json +50 -0
- drowse/data/manifolds/traditional.progressive/nodes/01_progressive.json +50 -0
- drowse/data/manifolds/verbose.concise/manifold.json +28 -0
- drowse/data/manifolds/verbose.concise/nodes/00_verbose.json +50 -0
- drowse/data/manifolds/verbose.concise/nodes/01_concise.json +50 -0
- drowse/data/manifolds/warm.clinical/manifold.json +28 -0
- drowse/data/manifolds/warm.clinical/nodes/00_warm.json +50 -0
- drowse/data/manifolds/warm.clinical/nodes/01_clinical.json +50 -0
- drowse/data/neutral_statements.json +98 -0
- drowse/io/__init__.py +1 -0
- drowse/io/alignment.py +1334 -0
- drowse/io/atomic.py +278 -0
- drowse/io/bake.py +558 -0
- drowse/io/bootstrap.py +73 -0
- drowse/io/brand_migration.py +48 -0
- drowse/io/drowse_archive.py +1830 -0
- drowse/io/gguf_io.py +300 -0
- drowse/io/hf.py +81 -0
- drowse/io/hf_manifolds.py +931 -0
- drowse/io/integrity.py +102 -0
- drowse/io/lens.py +1992 -0
- drowse/io/lens_sources.py +1003 -0
- drowse/io/manifold_authoring.py +1348 -0
- drowse/io/manifold_folder.py +1854 -0
- drowse/io/manifold_lifecycle.py +1253 -0
- drowse/io/manifold_tensors.py +727 -0
- drowse/io/manifolds.py +443 -0
- drowse/io/paths.py +289 -0
- drowse/io/probes_bootstrap.py +58 -0
- drowse/io/sae.py +382 -0
- drowse/io/sae_artifacts.py +318 -0
- drowse/io/selectors.py +503 -0
- drowse/io/shards.py +160 -0
- drowse/io/source_registry.py +138 -0
- drowse/io/staging.py +80 -0
- drowse/io/templates.py +707 -0
- drowse/notebook/__init__.py +38 -0
- drowse/notebook/data.py +99 -0
- drowse/notebook/plots.py +352 -0
- drowse/py.typed +0 -0
- drowse/server/__init__.py +20 -0
- drowse/server/app.py +987 -0
- drowse/server/background_job.py +246 -0
- drowse/server/instrument_routes.py +1101 -0
- drowse/server/manifold_routes.py +1028 -0
- drowse/server/model_names.py +82 -0
- drowse/server/native_common.py +92 -0
- drowse/server/native_routes.py +45 -0
- drowse/server/ollama.py +817 -0
- drowse/server/operation_ledger.py +187 -0
- drowse/server/probe_routes.py +220 -0
- drowse/server/profile_models.py +52 -0
- drowse/server/profile_routes.py +440 -0
- drowse/server/request_helpers.py +198 -0
- drowse/server/request_ledger.py +85 -0
- drowse/server/response_models.py +814 -0
- drowse/server/session_models.py +146 -0
- drowse/server/session_routes.py +150 -0
- drowse/server/sse.py +333 -0
- drowse/server/streaming.py +129 -0
- drowse/server/template_routes.py +161 -0
- drowse/server/tree_models.py +121 -0
- drowse/server/tree_routes.py +550 -0
- drowse/server/ws_events.py +65 -0
- drowse/server/ws_models.py +219 -0
- drowse/server/ws_stream.py +1205 -0
- drowse/web/__init__.py +22 -0
- drowse/web/dist/LICENSE-Fluent-Icons.txt +21 -0
- drowse/web/dist/LICENSE-Fluent-System-Icons.txt +21 -0
- drowse/web/dist/LICENSE-Martian-Mono.txt +93 -0
- drowse/web/dist/LICENSE-Torph.txt +21 -0
- drowse/web/dist/LICENSE-Wix-Madefor.txt +93 -0
- drowse/web/dist/assets/App-BewwoFwv.js +33 -0
- drowse/web/dist/assets/App.css +1 -0
- drowse/web/dist/assets/MartianMono-cyrillic-ext.woff2 +0 -0
- drowse/web/dist/assets/MartianMono-cyrillic.woff2 +0 -0
- drowse/web/dist/assets/MartianMono-latin-ext.woff2 +0 -0
- drowse/web/dist/assets/MartianMono-latin.woff2 +0 -0
- drowse/web/dist/assets/WixMadeforDisplay-cyrillic.woff2 +0 -0
- drowse/web/dist/assets/WixMadeforDisplay-latin-ext.woff2 +0 -0
- drowse/web/dist/assets/WixMadeforDisplay-latin.woff2 +0 -0
- drowse/web/dist/assets/WixMadeforDisplay-vietnamese.woff2 +0 -0
- drowse/web/dist/assets/WixMadeforText-italic-latin-ext.woff2 +0 -0
- drowse/web/dist/assets/WixMadeforText-italic-latin.woff2 +0 -0
- drowse/web/dist/assets/WixMadeforText-normal-cyrillic.woff2 +0 -0
- drowse/web/dist/assets/WixMadeforText-normal-latin-ext.woff2 +0 -0
- drowse/web/dist/assets/WixMadeforText-normal-latin.woff2 +0 -0
- drowse/web/dist/assets/WixMadeforText-normal-vietnamese.woff2 +0 -0
- drowse/web/dist/assets/ab.svelte-CbVGAYXo.js +1 -0
- drowse/web/dist/assets/artifactUpdates-Cjeu6LJc.js +1 -0
- drowse/web/dist/assets/artifactUpdates-DfAoYC9j.js +1 -0
- drowse/web/dist/assets/chat.svelte-DxKufn9g.js +1 -0
- drowse/web/dist/assets/drawers-BDWC0hIN.js +123 -0
- drowse/web/dist/assets/drawers.css +1 -0
- drowse/web/dist/assets/drowse.js +7 -0
- drowse/web/dist/assets/eleutherai.png +0 -0
- drowse/web/dist/assets/errors-Dvb10QW_.js +2 -0
- drowse/web/dist/assets/expression-4yISdAdq.js +1 -0
- drowse/web/dist/assets/files-CvRivkey.js +1 -0
- drowse/web/dist/assets/files-JhDJVgD_.js +1 -0
- drowse/web/dist/assets/fittingCancellation-D72BZWPZ.js +1 -0
- drowse/web/dist/assets/gemma-3-1b-it.json +1 -0
- drowse/web/dist/assets/gemma-3-270m-it.json +1 -0
- drowse/web/dist/assets/gemma-3-4b-it.json +1 -0
- drowse/web/dist/assets/generation-DbkBAhP_.js +1 -0
- drowse/web/dist/assets/index-client-2OdH6IQp.js +3 -0
- drowse/web/dist/assets/index-client-D4S4PooY.js +1 -0
- drowse/web/dist/assets/index.css +1 -0
- drowse/web/dist/assets/instrumentAuthoring.svelte-Csh7NOGX.js +1 -0
- drowse/web/dist/assets/instrumentAuthoring.svelte-w9YPcDsP.js +1 -0
- drowse/web/dist/assets/instruments.svelte-XnEUQA6B.js +1 -0
- drowse/web/dist/assets/localData-DIBDM_zo.js +1 -0
- drowse/web/dist/assets/loom.svelte-D_YyqHHK.js +1 -0
- drowse/web/dist/assets/manifoldJobs.svelte-9f5hs_J7.js +1 -0
- drowse/web/dist/assets/manifoldJobs.svelte-Bwo2GYOH.js +1 -0
- drowse/web/dist/assets/openai.png +0 -0
- drowse/web/dist/assets/preload-helper-BLdHbfdN.js +1 -0
- drowse/web/dist/assets/probes.svelte-COcN1Rh8.js +1 -0
- drowse/web/dist/assets/registry-B0_pJoMg.js +1 -0
- drowse/web/dist/assets/registry-CQv71XNh.js +1 -0
- drowse/web/dist/assets/sampling.svelte-BU0kED3k.js +1 -0
- drowse/web/dist/assets/samplingCapabilities-DM6eoDoc.js +1 -0
- drowse/web/dist/assets/savedConversations.svelte-BsBLc-p7.js +1 -0
- drowse/web/dist/assets/savedConversations.svelte-TuSes9Kg.js +1 -0
- drowse/web/dist/assets/selectors-BxXnPPN_.js +1 -0
- drowse/web/dist/assets/serviceTools-CG0q1Xqg.js +2 -0
- drowse/web/dist/assets/session.svelte-D2STLuP6.js +1 -0
- drowse/web/dist/assets/steering.svelte-DvFT3O-d.js +1 -0
- drowse/web/dist/assets/steeringPositions-Dr0JOFu7.js +1 -0
- drowse/web/dist/assets/templates-Bkq2x5ID.js +1 -0
- drowse/web/dist/assets/types-CuMUbvHW.js +1 -0
- drowse/web/dist/assets/validation-5wgTuS7k.js +1 -0
- drowse/web/dist/assets/webmcp-DyqJf4z6.js +2 -0
- drowse/web/dist/assets/workspaceController-C2L33e3m.js +1 -0
- drowse/web/dist/assets/workspaceController-DJhXD_BC.js +1 -0
- drowse/web/dist/assets/workspaceTools-BWdmXopx.js +2 -0
- drowse/web/dist/favicon.ico +0 -0
- drowse/web/dist/favicon.svg +1 -0
- drowse/web/dist/icons/apple-touch-icon.png +0 -0
- drowse/web/dist/icons/drowse-192.png +0 -0
- drowse/web/dist/icons/drowse-512.png +0 -0
- drowse/web/dist/icons/drowse-maskable-512.png +0 -0
- drowse/web/dist/icons/tab-analyzing-0.png +0 -0
- drowse/web/dist/icons/tab-analyzing-1.png +0 -0
- drowse/web/dist/icons/tab-analyzing-2.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-0.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-1.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-10.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-11.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-12.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-13.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-14.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-15.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-16.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-17.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-18.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-19.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-2.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-20.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-21.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-22.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-23.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-24.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-25.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-26.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-27.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-28.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-29.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-3.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-30.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-31.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-32.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-33.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-34.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-35.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-36.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-37.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-38.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-39.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-4.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-40.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-41.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-42.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-43.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-44.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-45.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-46.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-47.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-5.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-6.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-7.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-8.png +0 -0
- drowse/web/dist/icons/tab-analyzing-dark-9.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-0.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-1.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-10.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-11.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-12.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-13.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-14.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-15.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-16.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-17.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-18.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-19.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-2.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-20.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-21.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-22.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-23.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-24.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-25.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-26.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-27.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-28.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-29.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-3.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-30.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-31.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-32.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-33.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-34.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-35.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-36.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-37.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-38.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-39.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-4.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-40.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-41.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-42.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-43.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-44.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-45.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-46.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-47.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-5.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-6.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-7.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-8.png +0 -0
- drowse/web/dist/icons/tab-analyzing-light-9.png +0 -0
- drowse/web/dist/icons/tab-chat-settings-dark.png +0 -0
- drowse/web/dist/icons/tab-chat-settings-light.png +0 -0
- drowse/web/dist/icons/tab-chat-settings.png +0 -0
- drowse/web/dist/icons/tab-chats-dark.png +0 -0
- drowse/web/dist/icons/tab-chats-light.png +0 -0
- drowse/web/dist/icons/tab-chats.png +0 -0
- drowse/web/dist/icons/tab-checking-0.png +0 -0
- drowse/web/dist/icons/tab-checking-1.png +0 -0
- drowse/web/dist/icons/tab-checking-2.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-0.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-1.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-10.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-11.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-12.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-13.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-14.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-15.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-16.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-17.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-18.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-19.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-2.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-20.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-21.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-22.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-23.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-24.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-25.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-26.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-27.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-28.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-29.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-3.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-30.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-31.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-32.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-33.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-34.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-35.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-36.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-37.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-38.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-39.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-4.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-40.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-41.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-42.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-43.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-44.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-45.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-46.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-47.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-5.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-6.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-7.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-8.png +0 -0
- drowse/web/dist/icons/tab-checking-dark-9.png +0 -0
- drowse/web/dist/icons/tab-checking-light-0.png +0 -0
- drowse/web/dist/icons/tab-checking-light-1.png +0 -0
- drowse/web/dist/icons/tab-checking-light-10.png +0 -0
- drowse/web/dist/icons/tab-checking-light-11.png +0 -0
- drowse/web/dist/icons/tab-checking-light-12.png +0 -0
- drowse/web/dist/icons/tab-checking-light-13.png +0 -0
- drowse/web/dist/icons/tab-checking-light-14.png +0 -0
- drowse/web/dist/icons/tab-checking-light-15.png +0 -0
- drowse/web/dist/icons/tab-checking-light-16.png +0 -0
- drowse/web/dist/icons/tab-checking-light-17.png +0 -0
- drowse/web/dist/icons/tab-checking-light-18.png +0 -0
- drowse/web/dist/icons/tab-checking-light-19.png +0 -0
- drowse/web/dist/icons/tab-checking-light-2.png +0 -0
- drowse/web/dist/icons/tab-checking-light-20.png +0 -0
- drowse/web/dist/icons/tab-checking-light-21.png +0 -0
- drowse/web/dist/icons/tab-checking-light-22.png +0 -0
- drowse/web/dist/icons/tab-checking-light-23.png +0 -0
- drowse/web/dist/icons/tab-checking-light-24.png +0 -0
- drowse/web/dist/icons/tab-checking-light-25.png +0 -0
- drowse/web/dist/icons/tab-checking-light-26.png +0 -0
- drowse/web/dist/icons/tab-checking-light-27.png +0 -0
- drowse/web/dist/icons/tab-checking-light-28.png +0 -0
- drowse/web/dist/icons/tab-checking-light-29.png +0 -0
- drowse/web/dist/icons/tab-checking-light-3.png +0 -0
- drowse/web/dist/icons/tab-checking-light-30.png +0 -0
- drowse/web/dist/icons/tab-checking-light-31.png +0 -0
- drowse/web/dist/icons/tab-checking-light-32.png +0 -0
- drowse/web/dist/icons/tab-checking-light-33.png +0 -0
- drowse/web/dist/icons/tab-checking-light-34.png +0 -0
- drowse/web/dist/icons/tab-checking-light-35.png +0 -0
- drowse/web/dist/icons/tab-checking-light-36.png +0 -0
- drowse/web/dist/icons/tab-checking-light-37.png +0 -0
- drowse/web/dist/icons/tab-checking-light-38.png +0 -0
- drowse/web/dist/icons/tab-checking-light-39.png +0 -0
- drowse/web/dist/icons/tab-checking-light-4.png +0 -0
- drowse/web/dist/icons/tab-checking-light-40.png +0 -0
- drowse/web/dist/icons/tab-checking-light-41.png +0 -0
- drowse/web/dist/icons/tab-checking-light-42.png +0 -0
- drowse/web/dist/icons/tab-checking-light-43.png +0 -0
- drowse/web/dist/icons/tab-checking-light-44.png +0 -0
- drowse/web/dist/icons/tab-checking-light-45.png +0 -0
- drowse/web/dist/icons/tab-checking-light-46.png +0 -0
- drowse/web/dist/icons/tab-checking-light-47.png +0 -0
- drowse/web/dist/icons/tab-checking-light-5.png +0 -0
- drowse/web/dist/icons/tab-checking-light-6.png +0 -0
- drowse/web/dist/icons/tab-checking-light-7.png +0 -0
- drowse/web/dist/icons/tab-checking-light-8.png +0 -0
- drowse/web/dist/icons/tab-checking-light-9.png +0 -0
- drowse/web/dist/icons/tab-comparison-dark.png +0 -0
- drowse/web/dist/icons/tab-comparison-light.png +0 -0
- drowse/web/dist/icons/tab-comparison.png +0 -0
- drowse/web/dist/icons/tab-controls-dark.png +0 -0
- drowse/web/dist/icons/tab-controls-light.png +0 -0
- drowse/web/dist/icons/tab-controls.png +0 -0
- drowse/web/dist/icons/tab-conversation-dark.png +0 -0
- drowse/web/dist/icons/tab-conversation-light.png +0 -0
- drowse/web/dist/icons/tab-conversation.png +0 -0
- drowse/web/dist/icons/tab-credits-dark.png +0 -0
- drowse/web/dist/icons/tab-credits-light.png +0 -0
- drowse/web/dist/icons/tab-credits.png +0 -0
- drowse/web/dist/icons/tab-downloading-0.png +0 -0
- drowse/web/dist/icons/tab-downloading-1.png +0 -0
- drowse/web/dist/icons/tab-downloading-2.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-0.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-1.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-10.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-11.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-12.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-13.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-14.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-15.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-16.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-17.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-18.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-19.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-2.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-20.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-21.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-22.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-23.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-24.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-25.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-26.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-27.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-28.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-29.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-3.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-30.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-31.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-32.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-33.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-34.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-35.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-36.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-37.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-38.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-39.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-4.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-40.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-41.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-42.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-43.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-44.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-45.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-46.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-47.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-5.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-6.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-7.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-8.png +0 -0
- drowse/web/dist/icons/tab-downloading-dark-9.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-0.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-1.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-10.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-11.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-12.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-13.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-14.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-15.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-16.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-17.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-18.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-19.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-2.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-20.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-21.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-22.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-23.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-24.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-25.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-26.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-27.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-28.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-29.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-3.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-30.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-31.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-32.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-33.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-34.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-35.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-36.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-37.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-38.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-39.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-4.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-40.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-41.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-42.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-43.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-44.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-45.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-46.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-47.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-5.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-6.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-7.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-8.png +0 -0
- drowse/web/dist/icons/tab-downloading-light-9.png +0 -0
- drowse/web/dist/icons/tab-error-dark.png +0 -0
- drowse/web/dist/icons/tab-error-light.png +0 -0
- drowse/web/dist/icons/tab-error.png +0 -0
- drowse/web/dist/icons/tab-home-dark.png +0 -0
- drowse/web/dist/icons/tab-home-light.png +0 -0
- drowse/web/dist/icons/tab-home.png +0 -0
- drowse/web/dist/icons/tab-loading-0.png +0 -0
- drowse/web/dist/icons/tab-loading-1.png +0 -0
- drowse/web/dist/icons/tab-loading-2.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-0.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-1.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-10.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-11.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-12.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-13.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-14.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-15.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-16.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-17.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-18.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-19.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-2.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-20.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-21.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-22.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-23.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-24.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-25.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-26.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-27.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-28.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-29.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-3.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-30.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-31.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-32.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-33.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-34.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-35.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-36.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-37.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-38.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-39.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-4.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-40.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-41.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-42.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-43.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-44.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-45.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-46.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-47.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-5.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-6.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-7.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-8.png +0 -0
- drowse/web/dist/icons/tab-loading-dark-9.png +0 -0
- drowse/web/dist/icons/tab-loading-light-0.png +0 -0
- drowse/web/dist/icons/tab-loading-light-1.png +0 -0
- drowse/web/dist/icons/tab-loading-light-10.png +0 -0
- drowse/web/dist/icons/tab-loading-light-11.png +0 -0
- drowse/web/dist/icons/tab-loading-light-12.png +0 -0
- drowse/web/dist/icons/tab-loading-light-13.png +0 -0
- drowse/web/dist/icons/tab-loading-light-14.png +0 -0
- drowse/web/dist/icons/tab-loading-light-15.png +0 -0
- drowse/web/dist/icons/tab-loading-light-16.png +0 -0
- drowse/web/dist/icons/tab-loading-light-17.png +0 -0
- drowse/web/dist/icons/tab-loading-light-18.png +0 -0
- drowse/web/dist/icons/tab-loading-light-19.png +0 -0
- drowse/web/dist/icons/tab-loading-light-2.png +0 -0
- drowse/web/dist/icons/tab-loading-light-20.png +0 -0
- drowse/web/dist/icons/tab-loading-light-21.png +0 -0
- drowse/web/dist/icons/tab-loading-light-22.png +0 -0
- drowse/web/dist/icons/tab-loading-light-23.png +0 -0
- drowse/web/dist/icons/tab-loading-light-24.png +0 -0
- drowse/web/dist/icons/tab-loading-light-25.png +0 -0
- drowse/web/dist/icons/tab-loading-light-26.png +0 -0
- drowse/web/dist/icons/tab-loading-light-27.png +0 -0
- drowse/web/dist/icons/tab-loading-light-28.png +0 -0
- drowse/web/dist/icons/tab-loading-light-29.png +0 -0
- drowse/web/dist/icons/tab-loading-light-3.png +0 -0
- drowse/web/dist/icons/tab-loading-light-30.png +0 -0
- drowse/web/dist/icons/tab-loading-light-31.png +0 -0
- drowse/web/dist/icons/tab-loading-light-32.png +0 -0
- drowse/web/dist/icons/tab-loading-light-33.png +0 -0
- drowse/web/dist/icons/tab-loading-light-34.png +0 -0
- drowse/web/dist/icons/tab-loading-light-35.png +0 -0
- drowse/web/dist/icons/tab-loading-light-36.png +0 -0
- drowse/web/dist/icons/tab-loading-light-37.png +0 -0
- drowse/web/dist/icons/tab-loading-light-38.png +0 -0
- drowse/web/dist/icons/tab-loading-light-39.png +0 -0
- drowse/web/dist/icons/tab-loading-light-4.png +0 -0
- drowse/web/dist/icons/tab-loading-light-40.png +0 -0
- drowse/web/dist/icons/tab-loading-light-41.png +0 -0
- drowse/web/dist/icons/tab-loading-light-42.png +0 -0
- drowse/web/dist/icons/tab-loading-light-43.png +0 -0
- drowse/web/dist/icons/tab-loading-light-44.png +0 -0
- drowse/web/dist/icons/tab-loading-light-45.png +0 -0
- drowse/web/dist/icons/tab-loading-light-46.png +0 -0
- drowse/web/dist/icons/tab-loading-light-47.png +0 -0
- drowse/web/dist/icons/tab-loading-light-5.png +0 -0
- drowse/web/dist/icons/tab-loading-light-6.png +0 -0
- drowse/web/dist/icons/tab-loading-light-7.png +0 -0
- drowse/web/dist/icons/tab-loading-light-8.png +0 -0
- drowse/web/dist/icons/tab-loading-light-9.png +0 -0
- drowse/web/dist/icons/tab-loom-dark.png +0 -0
- drowse/web/dist/icons/tab-loom-light.png +0 -0
- drowse/web/dist/icons/tab-loom-working-0.png +0 -0
- drowse/web/dist/icons/tab-loom-working-1.png +0 -0
- drowse/web/dist/icons/tab-loom-working-2.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-0.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-1.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-10.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-11.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-12.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-13.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-14.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-15.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-16.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-17.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-18.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-19.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-2.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-20.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-21.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-22.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-23.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-24.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-25.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-26.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-27.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-28.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-29.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-3.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-30.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-31.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-32.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-33.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-34.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-35.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-36.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-37.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-38.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-39.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-4.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-40.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-41.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-42.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-43.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-44.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-45.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-46.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-47.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-5.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-6.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-7.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-8.png +0 -0
- drowse/web/dist/icons/tab-loom-working-dark-9.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-0.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-1.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-10.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-11.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-12.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-13.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-14.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-15.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-16.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-17.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-18.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-19.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-2.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-20.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-21.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-22.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-23.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-24.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-25.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-26.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-27.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-28.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-29.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-3.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-30.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-31.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-32.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-33.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-34.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-35.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-36.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-37.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-38.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-39.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-4.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-40.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-41.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-42.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-43.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-44.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-45.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-46.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-47.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-5.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-6.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-7.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-8.png +0 -0
- drowse/web/dist/icons/tab-loom-working-light-9.png +0 -0
- drowse/web/dist/icons/tab-loom.png +0 -0
- drowse/web/dist/icons/tab-models-dark.png +0 -0
- drowse/web/dist/icons/tab-models-light.png +0 -0
- drowse/web/dist/icons/tab-models.png +0 -0
- drowse/web/dist/icons/tab-offline-dark.png +0 -0
- drowse/web/dist/icons/tab-offline-light.png +0 -0
- drowse/web/dist/icons/tab-offline.png +0 -0
- drowse/web/dist/icons/tab-paused-dark.png +0 -0
- drowse/web/dist/icons/tab-paused-light.png +0 -0
- drowse/web/dist/icons/tab-paused.png +0 -0
- drowse/web/dist/icons/tab-ready-dark.png +0 -0
- drowse/web/dist/icons/tab-ready-light.png +0 -0
- drowse/web/dist/icons/tab-ready.png +0 -0
- drowse/web/dist/icons/tab-saving-0.png +0 -0
- drowse/web/dist/icons/tab-saving-1.png +0 -0
- drowse/web/dist/icons/tab-saving-2.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-0.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-1.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-10.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-11.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-12.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-13.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-14.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-15.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-16.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-17.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-18.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-19.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-2.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-20.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-21.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-22.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-23.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-24.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-25.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-26.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-27.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-28.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-29.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-3.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-30.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-31.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-32.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-33.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-34.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-35.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-36.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-37.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-38.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-39.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-4.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-40.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-41.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-42.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-43.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-44.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-45.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-46.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-47.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-5.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-6.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-7.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-8.png +0 -0
- drowse/web/dist/icons/tab-saving-dark-9.png +0 -0
- drowse/web/dist/icons/tab-saving-light-0.png +0 -0
- drowse/web/dist/icons/tab-saving-light-1.png +0 -0
- drowse/web/dist/icons/tab-saving-light-10.png +0 -0
- drowse/web/dist/icons/tab-saving-light-11.png +0 -0
- drowse/web/dist/icons/tab-saving-light-12.png +0 -0
- drowse/web/dist/icons/tab-saving-light-13.png +0 -0
- drowse/web/dist/icons/tab-saving-light-14.png +0 -0
- drowse/web/dist/icons/tab-saving-light-15.png +0 -0
- drowse/web/dist/icons/tab-saving-light-16.png +0 -0
- drowse/web/dist/icons/tab-saving-light-17.png +0 -0
- drowse/web/dist/icons/tab-saving-light-18.png +0 -0
- drowse/web/dist/icons/tab-saving-light-19.png +0 -0
- drowse/web/dist/icons/tab-saving-light-2.png +0 -0
- drowse/web/dist/icons/tab-saving-light-20.png +0 -0
- drowse/web/dist/icons/tab-saving-light-21.png +0 -0
- drowse/web/dist/icons/tab-saving-light-22.png +0 -0
- drowse/web/dist/icons/tab-saving-light-23.png +0 -0
- drowse/web/dist/icons/tab-saving-light-24.png +0 -0
- drowse/web/dist/icons/tab-saving-light-25.png +0 -0
- drowse/web/dist/icons/tab-saving-light-26.png +0 -0
- drowse/web/dist/icons/tab-saving-light-27.png +0 -0
- drowse/web/dist/icons/tab-saving-light-28.png +0 -0
- drowse/web/dist/icons/tab-saving-light-29.png +0 -0
- drowse/web/dist/icons/tab-saving-light-3.png +0 -0
- drowse/web/dist/icons/tab-saving-light-30.png +0 -0
- drowse/web/dist/icons/tab-saving-light-31.png +0 -0
- drowse/web/dist/icons/tab-saving-light-32.png +0 -0
- drowse/web/dist/icons/tab-saving-light-33.png +0 -0
- drowse/web/dist/icons/tab-saving-light-34.png +0 -0
- drowse/web/dist/icons/tab-saving-light-35.png +0 -0
- drowse/web/dist/icons/tab-saving-light-36.png +0 -0
- drowse/web/dist/icons/tab-saving-light-37.png +0 -0
- drowse/web/dist/icons/tab-saving-light-38.png +0 -0
- drowse/web/dist/icons/tab-saving-light-39.png +0 -0
- drowse/web/dist/icons/tab-saving-light-4.png +0 -0
- drowse/web/dist/icons/tab-saving-light-40.png +0 -0
- drowse/web/dist/icons/tab-saving-light-41.png +0 -0
- drowse/web/dist/icons/tab-saving-light-42.png +0 -0
- drowse/web/dist/icons/tab-saving-light-43.png +0 -0
- drowse/web/dist/icons/tab-saving-light-44.png +0 -0
- drowse/web/dist/icons/tab-saving-light-45.png +0 -0
- drowse/web/dist/icons/tab-saving-light-46.png +0 -0
- drowse/web/dist/icons/tab-saving-light-47.png +0 -0
- drowse/web/dist/icons/tab-saving-light-5.png +0 -0
- drowse/web/dist/icons/tab-saving-light-6.png +0 -0
- drowse/web/dist/icons/tab-saving-light-7.png +0 -0
- drowse/web/dist/icons/tab-saving-light-8.png +0 -0
- drowse/web/dist/icons/tab-saving-light-9.png +0 -0
- drowse/web/dist/icons/tab-thinking-0.png +0 -0
- drowse/web/dist/icons/tab-thinking-1.png +0 -0
- drowse/web/dist/icons/tab-thinking-2.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-0.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-1.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-10.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-11.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-12.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-13.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-14.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-15.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-16.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-17.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-18.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-19.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-2.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-20.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-21.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-22.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-23.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-24.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-25.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-26.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-27.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-28.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-29.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-3.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-30.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-31.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-32.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-33.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-34.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-35.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-36.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-37.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-38.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-39.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-4.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-40.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-41.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-42.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-43.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-44.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-45.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-46.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-47.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-5.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-6.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-7.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-8.png +0 -0
- drowse/web/dist/icons/tab-thinking-dark-9.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-0.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-1.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-10.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-11.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-12.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-13.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-14.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-15.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-16.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-17.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-18.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-19.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-2.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-20.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-21.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-22.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-23.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-24.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-25.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-26.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-27.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-28.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-29.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-3.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-30.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-31.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-32.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-33.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-34.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-35.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-36.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-37.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-38.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-39.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-4.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-40.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-41.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-42.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-43.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-44.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-45.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-46.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-47.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-5.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-6.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-7.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-8.png +0 -0
- drowse/web/dist/icons/tab-thinking-light-9.png +0 -0
- drowse/web/dist/icons/tab-tokens-dark.png +0 -0
- drowse/web/dist/icons/tab-tokens-light.png +0 -0
- drowse/web/dist/icons/tab-tokens.png +0 -0
- drowse/web/dist/icons/tab-training-0.png +0 -0
- drowse/web/dist/icons/tab-training-1.png +0 -0
- drowse/web/dist/icons/tab-training-2.png +0 -0
- drowse/web/dist/icons/tab-training-dark-0.png +0 -0
- drowse/web/dist/icons/tab-training-dark-1.png +0 -0
- drowse/web/dist/icons/tab-training-dark-10.png +0 -0
- drowse/web/dist/icons/tab-training-dark-11.png +0 -0
- drowse/web/dist/icons/tab-training-dark-12.png +0 -0
- drowse/web/dist/icons/tab-training-dark-13.png +0 -0
- drowse/web/dist/icons/tab-training-dark-14.png +0 -0
- drowse/web/dist/icons/tab-training-dark-15.png +0 -0
- drowse/web/dist/icons/tab-training-dark-16.png +0 -0
- drowse/web/dist/icons/tab-training-dark-17.png +0 -0
- drowse/web/dist/icons/tab-training-dark-18.png +0 -0
- drowse/web/dist/icons/tab-training-dark-19.png +0 -0
- drowse/web/dist/icons/tab-training-dark-2.png +0 -0
- drowse/web/dist/icons/tab-training-dark-20.png +0 -0
- drowse/web/dist/icons/tab-training-dark-21.png +0 -0
- drowse/web/dist/icons/tab-training-dark-22.png +0 -0
- drowse/web/dist/icons/tab-training-dark-23.png +0 -0
- drowse/web/dist/icons/tab-training-dark-24.png +0 -0
- drowse/web/dist/icons/tab-training-dark-25.png +0 -0
- drowse/web/dist/icons/tab-training-dark-26.png +0 -0
- drowse/web/dist/icons/tab-training-dark-27.png +0 -0
- drowse/web/dist/icons/tab-training-dark-28.png +0 -0
- drowse/web/dist/icons/tab-training-dark-29.png +0 -0
- drowse/web/dist/icons/tab-training-dark-3.png +0 -0
- drowse/web/dist/icons/tab-training-dark-30.png +0 -0
- drowse/web/dist/icons/tab-training-dark-31.png +0 -0
- drowse/web/dist/icons/tab-training-dark-32.png +0 -0
- drowse/web/dist/icons/tab-training-dark-33.png +0 -0
- drowse/web/dist/icons/tab-training-dark-34.png +0 -0
- drowse/web/dist/icons/tab-training-dark-35.png +0 -0
- drowse/web/dist/icons/tab-training-dark-36.png +0 -0
- drowse/web/dist/icons/tab-training-dark-37.png +0 -0
- drowse/web/dist/icons/tab-training-dark-38.png +0 -0
- drowse/web/dist/icons/tab-training-dark-39.png +0 -0
- drowse/web/dist/icons/tab-training-dark-4.png +0 -0
- drowse/web/dist/icons/tab-training-dark-40.png +0 -0
- drowse/web/dist/icons/tab-training-dark-41.png +0 -0
- drowse/web/dist/icons/tab-training-dark-42.png +0 -0
- drowse/web/dist/icons/tab-training-dark-43.png +0 -0
- drowse/web/dist/icons/tab-training-dark-44.png +0 -0
- drowse/web/dist/icons/tab-training-dark-45.png +0 -0
- drowse/web/dist/icons/tab-training-dark-46.png +0 -0
- drowse/web/dist/icons/tab-training-dark-47.png +0 -0
- drowse/web/dist/icons/tab-training-dark-5.png +0 -0
- drowse/web/dist/icons/tab-training-dark-6.png +0 -0
- drowse/web/dist/icons/tab-training-dark-7.png +0 -0
- drowse/web/dist/icons/tab-training-dark-8.png +0 -0
- drowse/web/dist/icons/tab-training-dark-9.png +0 -0
- drowse/web/dist/icons/tab-training-light-0.png +0 -0
- drowse/web/dist/icons/tab-training-light-1.png +0 -0
- drowse/web/dist/icons/tab-training-light-10.png +0 -0
- drowse/web/dist/icons/tab-training-light-11.png +0 -0
- drowse/web/dist/icons/tab-training-light-12.png +0 -0
- drowse/web/dist/icons/tab-training-light-13.png +0 -0
- drowse/web/dist/icons/tab-training-light-14.png +0 -0
- drowse/web/dist/icons/tab-training-light-15.png +0 -0
- drowse/web/dist/icons/tab-training-light-16.png +0 -0
- drowse/web/dist/icons/tab-training-light-17.png +0 -0
- drowse/web/dist/icons/tab-training-light-18.png +0 -0
- drowse/web/dist/icons/tab-training-light-19.png +0 -0
- drowse/web/dist/icons/tab-training-light-2.png +0 -0
- drowse/web/dist/icons/tab-training-light-20.png +0 -0
- drowse/web/dist/icons/tab-training-light-21.png +0 -0
- drowse/web/dist/icons/tab-training-light-22.png +0 -0
- drowse/web/dist/icons/tab-training-light-23.png +0 -0
- drowse/web/dist/icons/tab-training-light-24.png +0 -0
- drowse/web/dist/icons/tab-training-light-25.png +0 -0
- drowse/web/dist/icons/tab-training-light-26.png +0 -0
- drowse/web/dist/icons/tab-training-light-27.png +0 -0
- drowse/web/dist/icons/tab-training-light-28.png +0 -0
- drowse/web/dist/icons/tab-training-light-29.png +0 -0
- drowse/web/dist/icons/tab-training-light-3.png +0 -0
- drowse/web/dist/icons/tab-training-light-30.png +0 -0
- drowse/web/dist/icons/tab-training-light-31.png +0 -0
- drowse/web/dist/icons/tab-training-light-32.png +0 -0
- drowse/web/dist/icons/tab-training-light-33.png +0 -0
- drowse/web/dist/icons/tab-training-light-34.png +0 -0
- drowse/web/dist/icons/tab-training-light-35.png +0 -0
- drowse/web/dist/icons/tab-training-light-36.png +0 -0
- drowse/web/dist/icons/tab-training-light-37.png +0 -0
- drowse/web/dist/icons/tab-training-light-38.png +0 -0
- drowse/web/dist/icons/tab-training-light-39.png +0 -0
- drowse/web/dist/icons/tab-training-light-4.png +0 -0
- drowse/web/dist/icons/tab-training-light-40.png +0 -0
- drowse/web/dist/icons/tab-training-light-41.png +0 -0
- drowse/web/dist/icons/tab-training-light-42.png +0 -0
- drowse/web/dist/icons/tab-training-light-43.png +0 -0
- drowse/web/dist/icons/tab-training-light-44.png +0 -0
- drowse/web/dist/icons/tab-training-light-45.png +0 -0
- drowse/web/dist/icons/tab-training-light-46.png +0 -0
- drowse/web/dist/icons/tab-training-light-47.png +0 -0
- drowse/web/dist/icons/tab-training-light-5.png +0 -0
- drowse/web/dist/icons/tab-training-light-6.png +0 -0
- drowse/web/dist/icons/tab-training-light-7.png +0 -0
- drowse/web/dist/icons/tab-training-light-8.png +0 -0
- drowse/web/dist/icons/tab-training-light-9.png +0 -0
- drowse/web/dist/icons/tab-unsupported-dark.png +0 -0
- drowse/web/dist/icons/tab-unsupported-light.png +0 -0
- drowse/web/dist/icons/tab-unsupported.png +0 -0
- drowse/web/dist/icons/tab-working-0.png +0 -0
- drowse/web/dist/icons/tab-working-1.png +0 -0
- drowse/web/dist/icons/tab-working-2.png +0 -0
- drowse/web/dist/icons/tab-working-dark-0.png +0 -0
- drowse/web/dist/icons/tab-working-dark-1.png +0 -0
- drowse/web/dist/icons/tab-working-dark-10.png +0 -0
- drowse/web/dist/icons/tab-working-dark-11.png +0 -0
- drowse/web/dist/icons/tab-working-dark-12.png +0 -0
- drowse/web/dist/icons/tab-working-dark-13.png +0 -0
- drowse/web/dist/icons/tab-working-dark-14.png +0 -0
- drowse/web/dist/icons/tab-working-dark-15.png +0 -0
- drowse/web/dist/icons/tab-working-dark-16.png +0 -0
- drowse/web/dist/icons/tab-working-dark-17.png +0 -0
- drowse/web/dist/icons/tab-working-dark-18.png +0 -0
- drowse/web/dist/icons/tab-working-dark-19.png +0 -0
- drowse/web/dist/icons/tab-working-dark-2.png +0 -0
- drowse/web/dist/icons/tab-working-dark-20.png +0 -0
- drowse/web/dist/icons/tab-working-dark-21.png +0 -0
- drowse/web/dist/icons/tab-working-dark-22.png +0 -0
- drowse/web/dist/icons/tab-working-dark-23.png +0 -0
- drowse/web/dist/icons/tab-working-dark-24.png +0 -0
- drowse/web/dist/icons/tab-working-dark-25.png +0 -0
- drowse/web/dist/icons/tab-working-dark-26.png +0 -0
- drowse/web/dist/icons/tab-working-dark-27.png +0 -0
- drowse/web/dist/icons/tab-working-dark-28.png +0 -0
- drowse/web/dist/icons/tab-working-dark-29.png +0 -0
- drowse/web/dist/icons/tab-working-dark-3.png +0 -0
- drowse/web/dist/icons/tab-working-dark-30.png +0 -0
- drowse/web/dist/icons/tab-working-dark-31.png +0 -0
- drowse/web/dist/icons/tab-working-dark-32.png +0 -0
- drowse/web/dist/icons/tab-working-dark-33.png +0 -0
- drowse/web/dist/icons/tab-working-dark-34.png +0 -0
- drowse/web/dist/icons/tab-working-dark-35.png +0 -0
- drowse/web/dist/icons/tab-working-dark-36.png +0 -0
- drowse/web/dist/icons/tab-working-dark-37.png +0 -0
- drowse/web/dist/icons/tab-working-dark-38.png +0 -0
- drowse/web/dist/icons/tab-working-dark-39.png +0 -0
- drowse/web/dist/icons/tab-working-dark-4.png +0 -0
- drowse/web/dist/icons/tab-working-dark-40.png +0 -0
- drowse/web/dist/icons/tab-working-dark-41.png +0 -0
- drowse/web/dist/icons/tab-working-dark-42.png +0 -0
- drowse/web/dist/icons/tab-working-dark-43.png +0 -0
- drowse/web/dist/icons/tab-working-dark-44.png +0 -0
- drowse/web/dist/icons/tab-working-dark-45.png +0 -0
- drowse/web/dist/icons/tab-working-dark-46.png +0 -0
- drowse/web/dist/icons/tab-working-dark-47.png +0 -0
- drowse/web/dist/icons/tab-working-dark-5.png +0 -0
- drowse/web/dist/icons/tab-working-dark-6.png +0 -0
- drowse/web/dist/icons/tab-working-dark-7.png +0 -0
- drowse/web/dist/icons/tab-working-dark-8.png +0 -0
- drowse/web/dist/icons/tab-working-dark-9.png +0 -0
- drowse/web/dist/icons/tab-working-light-0.png +0 -0
- drowse/web/dist/icons/tab-working-light-1.png +0 -0
- drowse/web/dist/icons/tab-working-light-10.png +0 -0
- drowse/web/dist/icons/tab-working-light-11.png +0 -0
- drowse/web/dist/icons/tab-working-light-12.png +0 -0
- drowse/web/dist/icons/tab-working-light-13.png +0 -0
- drowse/web/dist/icons/tab-working-light-14.png +0 -0
- drowse/web/dist/icons/tab-working-light-15.png +0 -0
- drowse/web/dist/icons/tab-working-light-16.png +0 -0
- drowse/web/dist/icons/tab-working-light-17.png +0 -0
- drowse/web/dist/icons/tab-working-light-18.png +0 -0
- drowse/web/dist/icons/tab-working-light-19.png +0 -0
- drowse/web/dist/icons/tab-working-light-2.png +0 -0
- drowse/web/dist/icons/tab-working-light-20.png +0 -0
- drowse/web/dist/icons/tab-working-light-21.png +0 -0
- drowse/web/dist/icons/tab-working-light-22.png +0 -0
- drowse/web/dist/icons/tab-working-light-23.png +0 -0
- drowse/web/dist/icons/tab-working-light-24.png +0 -0
- drowse/web/dist/icons/tab-working-light-25.png +0 -0
- drowse/web/dist/icons/tab-working-light-26.png +0 -0
- drowse/web/dist/icons/tab-working-light-27.png +0 -0
- drowse/web/dist/icons/tab-working-light-28.png +0 -0
- drowse/web/dist/icons/tab-working-light-29.png +0 -0
- drowse/web/dist/icons/tab-working-light-3.png +0 -0
- drowse/web/dist/icons/tab-working-light-30.png +0 -0
- drowse/web/dist/icons/tab-working-light-31.png +0 -0
- drowse/web/dist/icons/tab-working-light-32.png +0 -0
- drowse/web/dist/icons/tab-working-light-33.png +0 -0
- drowse/web/dist/icons/tab-working-light-34.png +0 -0
- drowse/web/dist/icons/tab-working-light-35.png +0 -0
- drowse/web/dist/icons/tab-working-light-36.png +0 -0
- drowse/web/dist/icons/tab-working-light-37.png +0 -0
- drowse/web/dist/icons/tab-working-light-38.png +0 -0
- drowse/web/dist/icons/tab-working-light-39.png +0 -0
- drowse/web/dist/icons/tab-working-light-4.png +0 -0
- drowse/web/dist/icons/tab-working-light-40.png +0 -0
- drowse/web/dist/icons/tab-working-light-41.png +0 -0
- drowse/web/dist/icons/tab-working-light-42.png +0 -0
- drowse/web/dist/icons/tab-working-light-43.png +0 -0
- drowse/web/dist/icons/tab-working-light-44.png +0 -0
- drowse/web/dist/icons/tab-working-light-45.png +0 -0
- drowse/web/dist/icons/tab-working-light-46.png +0 -0
- drowse/web/dist/icons/tab-working-light-47.png +0 -0
- drowse/web/dist/icons/tab-working-light-5.png +0 -0
- drowse/web/dist/icons/tab-working-light-6.png +0 -0
- drowse/web/dist/icons/tab-working-light-7.png +0 -0
- drowse/web/dist/icons/tab-working-light-8.png +0 -0
- drowse/web/dist/icons/tab-working-light-9.png +0 -0
- drowse/web/dist/index.html +34 -0
- drowse/web/dist/social/drowse.png +0 -0
- drowse/web/dist/social/drowse.svg +6 -0
- drowse/web/dist/theme-init.js +25 -0
- drowse/web/routes.py +118 -0
- drowse_ai-5.3.1.dist-info/METADATA +632 -0
- drowse_ai-5.3.1.dist-info/RECORD +1341 -0
- drowse_ai-5.3.1.dist-info/WHEEL +5 -0
- drowse_ai-5.3.1.dist-info/entry_points.txt +2 -0
- drowse_ai-5.3.1.dist-info/licenses/LICENSE +661 -0
- drowse_ai-5.3.1.dist-info/top_level.txt +1 -0
drowse/server/app.py
ADDED
|
@@ -0,0 +1,987 @@
|
|
|
1
|
+
"""OpenAI-compatible API server backed by DrowseSession."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import base64
|
|
7
|
+
import hmac
|
|
8
|
+
import ipaddress
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
import os
|
|
12
|
+
import time
|
|
13
|
+
import uuid
|
|
14
|
+
from contextlib import asynccontextmanager
|
|
15
|
+
from typing import TYPE_CHECKING, Any, AsyncIterator, Callable, cast
|
|
16
|
+
from urllib.parse import parse_qsl, urlencode, urlsplit
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from drowse.core.results import GenerationResult
|
|
20
|
+
|
|
21
|
+
from fastapi import Depends, FastAPI, HTTPException, Request, WebSocket
|
|
22
|
+
from fastapi.exception_handlers import http_exception_handler
|
|
23
|
+
from fastapi.exceptions import RequestValidationError
|
|
24
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
25
|
+
from fastapi.responses import JSONResponse
|
|
26
|
+
from fastapi.security import HTTPBearer
|
|
27
|
+
from pydantic import BaseModel, Field, FiniteFloat, NonNegativeInt, model_validator
|
|
28
|
+
from starlette.datastructures import Headers, MutableHeaders
|
|
29
|
+
from starlette.exceptions import HTTPException as StarletteHTTPException
|
|
30
|
+
from starlette.types import ASGIApp, Message, Receive, Scope, Send
|
|
31
|
+
|
|
32
|
+
from drowse.core.errors import DrowseError
|
|
33
|
+
from drowse.core.session import ConcurrentGenerationError, GenerationStream, DrowseSession
|
|
34
|
+
from drowse.core.steering import Steering
|
|
35
|
+
from drowse.server.request_helpers import (
|
|
36
|
+
UnsupportedContentError,
|
|
37
|
+
build_sampling_config,
|
|
38
|
+
flatten_content,
|
|
39
|
+
merge_steering,
|
|
40
|
+
parse_request_steering,
|
|
41
|
+
probe_token_readings,
|
|
42
|
+
strict_model_enabled,
|
|
43
|
+
validate_logit_bias,
|
|
44
|
+
)
|
|
45
|
+
from drowse.server.streaming import (
|
|
46
|
+
ClosingStreamingResponse,
|
|
47
|
+
run_in_thread,
|
|
48
|
+
stream_events,
|
|
49
|
+
probe_reading_aggregate,
|
|
50
|
+
stream_finalizer,
|
|
51
|
+
usage_dict,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
SESSION_LOCK_TIMEOUT_SECONDS = 300
|
|
56
|
+
DEFAULT_MAX_REQUEST_BYTES = 64 * 1024 * 1024
|
|
57
|
+
|
|
58
|
+
#: Route-prefix discriminators for the three protocols served on one port.
|
|
59
|
+
#: Each owns an error envelope: OpenAI ``{"error": {message, type, param,
|
|
60
|
+
#: code}}``, Ollama ``{"error": "<msg>"}``, native ``{"detail": "<msg>"}``.
|
|
61
|
+
NATIVE_PREFIX = "/drowse/v1/"
|
|
62
|
+
OLLAMA_PREFIX = "/api/"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@asynccontextmanager
|
|
66
|
+
async def acquire_session_lock(session: DrowseSession) -> AsyncIterator[bool]:
|
|
67
|
+
"""Acquire ``session.lock`` with a 5-minute bound.
|
|
68
|
+
|
|
69
|
+
Yields ``True`` if the lock was obtained (released on exit) and
|
|
70
|
+
``False`` on timeout. Callers branch on the result to emit their
|
|
71
|
+
protocol-specific 503. Serializes all generation routes across both
|
|
72
|
+
the OpenAI and Ollama protocols on the same session.
|
|
73
|
+
"""
|
|
74
|
+
try:
|
|
75
|
+
async with asyncio.timeout(SESSION_LOCK_TIMEOUT_SECONDS):
|
|
76
|
+
await session.lock.acquire()
|
|
77
|
+
except (TimeoutError, asyncio.TimeoutError):
|
|
78
|
+
yield False
|
|
79
|
+
return
|
|
80
|
+
try:
|
|
81
|
+
yield True
|
|
82
|
+
finally:
|
|
83
|
+
session.lock.release()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# ---------------------------------------------------------------------------
|
|
87
|
+
# Pydantic request/response models
|
|
88
|
+
# ---------------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
class ChatMessage(BaseModel):
|
|
91
|
+
role: str
|
|
92
|
+
content: Any # str or list of content parts
|
|
93
|
+
name: str | None = None
|
|
94
|
+
|
|
95
|
+
@model_validator(mode="after")
|
|
96
|
+
def _normalize_content(self):
|
|
97
|
+
# Accept OpenAI multimodal content-part arrays for text-only use:
|
|
98
|
+
# concatenate text parts, reject anything else with a clear error.
|
|
99
|
+
self.content = flatten_content(self.content)
|
|
100
|
+
return self
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class StreamOptions(BaseModel):
|
|
104
|
+
include_usage: bool = False
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class _SamplingBase(BaseModel):
|
|
108
|
+
model: str | None = None
|
|
109
|
+
temperature: FiniteFloat | None = None
|
|
110
|
+
top_p: FiniteFloat | None = Field(default=None, ge=0, le=1)
|
|
111
|
+
max_tokens: int | None = Field(default=None, ge=0)
|
|
112
|
+
max_completion_tokens: int | None = Field(default=None, ge=0)
|
|
113
|
+
stream: bool = False
|
|
114
|
+
stream_options: StreamOptions | None = None
|
|
115
|
+
# Canonical native steering field — a steering expression string
|
|
116
|
+
# parsed through the shared grammar in
|
|
117
|
+
# :mod:`drowse.core.steering_expr`. Merged over the server's default
|
|
118
|
+
# :class:`Steering` and resolved through ``session.steering()`` so pole
|
|
119
|
+
# aliases and events fire via the single canonical resolver site.
|
|
120
|
+
steering: str | None = None
|
|
121
|
+
stop: str | list[str] | None = None
|
|
122
|
+
seed: int | None = None
|
|
123
|
+
logit_bias: dict[NonNegativeInt, FiniteFloat] | None = None
|
|
124
|
+
presence_penalty: FiniteFloat = 0.0
|
|
125
|
+
frequency_penalty: FiniteFloat = 0.0
|
|
126
|
+
logprobs: bool | int | None = None # chat: bool; completions: int
|
|
127
|
+
top_logprobs: int | None = None
|
|
128
|
+
user: str | None = None
|
|
129
|
+
# Native thinking override. None = auto (honours supports_thinking).
|
|
130
|
+
thinking: bool | None = None
|
|
131
|
+
# LangChain compat: accept no-op shapes, reject anything real.
|
|
132
|
+
tools: list[Any] | None = None
|
|
133
|
+
tool_choice: Any = None
|
|
134
|
+
# Fields accepted and ignored:
|
|
135
|
+
n: int | None = None
|
|
136
|
+
response_format: dict[str, Any] | None = None
|
|
137
|
+
|
|
138
|
+
@model_validator(mode="after")
|
|
139
|
+
def _unify_max_tokens(self):
|
|
140
|
+
if self.max_completion_tokens is not None and self.max_tokens is None:
|
|
141
|
+
self.max_tokens = self.max_completion_tokens
|
|
142
|
+
return self
|
|
143
|
+
|
|
144
|
+
@model_validator(mode="after")
|
|
145
|
+
def _check_langchain_compat(self):
|
|
146
|
+
# Accept `tools: []` / None silently; reject non-empty.
|
|
147
|
+
if self.tools:
|
|
148
|
+
raise UnsupportedContentError(
|
|
149
|
+
"tool calling is not supported by drowse"
|
|
150
|
+
)
|
|
151
|
+
# tool_choice: accept None, "none", "auto"; reject "required" and dicts.
|
|
152
|
+
tc = self.tool_choice
|
|
153
|
+
if tc is not None and tc not in ("none", "auto"):
|
|
154
|
+
raise UnsupportedContentError(
|
|
155
|
+
"tool_choice values other than 'none'/'auto' are not supported"
|
|
156
|
+
)
|
|
157
|
+
# response_format: accept None or {"type": "text"}; reject json modes.
|
|
158
|
+
rf = self.response_format
|
|
159
|
+
if rf is not None:
|
|
160
|
+
rf_type = rf.get("type")
|
|
161
|
+
if rf_type not in (None, "text"):
|
|
162
|
+
raise UnsupportedContentError(
|
|
163
|
+
"response_format types other than 'text' are not supported"
|
|
164
|
+
)
|
|
165
|
+
return self
|
|
166
|
+
|
|
167
|
+
def to_steering(
|
|
168
|
+
self, default_steering: "Steering | None",
|
|
169
|
+
) -> "Steering | None":
|
|
170
|
+
"""Compose ``self.steering`` (expression string) over the server default.
|
|
171
|
+
|
|
172
|
+
``None`` inherits the server default; an explicit empty string
|
|
173
|
+
clears it. Non-empty per-request expressions override the default
|
|
174
|
+
at the key level: alphas for concepts named in both the default
|
|
175
|
+
and the request come from the request; alphas only in the default
|
|
176
|
+
pass through. Returns ``None`` when the composed result is empty
|
|
177
|
+
and no ``thinking`` override was requested. Pole aliasing happens
|
|
178
|
+
inside ``session.steering()`` — the server does not resolve poles
|
|
179
|
+
here.
|
|
180
|
+
"""
|
|
181
|
+
req_steering, explicit_clear = parse_request_steering(self.steering)
|
|
182
|
+
thinking: bool | None = self.thinking
|
|
183
|
+
if req_steering is not None and req_steering.thinking is not None:
|
|
184
|
+
thinking = req_steering.thinking
|
|
185
|
+
return merge_steering(
|
|
186
|
+
req_steering, default_steering, explicit_clear, thinking,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class ChatCompletionRequest(_SamplingBase):
|
|
191
|
+
messages: list[ChatMessage]
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class CompletionRequest(_SamplingBase):
|
|
195
|
+
prompt: str
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
# ---------------------------------------------------------------------------
|
|
199
|
+
# Helpers
|
|
200
|
+
# ---------------------------------------------------------------------------
|
|
201
|
+
|
|
202
|
+
def _make_id() -> str:
|
|
203
|
+
return f"drowse-{uuid.uuid4().hex[:12]}"
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def openai_error_type(status: int) -> str:
|
|
207
|
+
"""Map an HTTP status onto OpenAI's ``error.type`` vocabulary.
|
|
208
|
+
|
|
209
|
+
One table for both OpenAI surfaces — the non-streaming exception handler
|
|
210
|
+
and the in-band SSE error frame — so a 404 / 409 / 422 reads the same on
|
|
211
|
+
either.
|
|
212
|
+
"""
|
|
213
|
+
if status == 409:
|
|
214
|
+
return "conflict"
|
|
215
|
+
if 400 <= status < 500:
|
|
216
|
+
return "invalid_request_error"
|
|
217
|
+
return "server_error"
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _error(status: int, message: str, error_type: str = "error",
|
|
221
|
+
param: str | None = None) -> JSONResponse:
|
|
222
|
+
return JSONResponse(
|
|
223
|
+
status_code=status,
|
|
224
|
+
content={"error": {"message": message, "type": error_type,
|
|
225
|
+
"param": param, "code": status}},
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _detail_text(detail: Any) -> str:
|
|
230
|
+
"""Flatten an ``HTTPException.detail`` to the native envelope's string."""
|
|
231
|
+
if isinstance(detail, str):
|
|
232
|
+
return detail
|
|
233
|
+
if isinstance(detail, dict):
|
|
234
|
+
for key in ("message", "msg", "detail"):
|
|
235
|
+
value = detail.get(key)
|
|
236
|
+
if isinstance(value, str):
|
|
237
|
+
return value
|
|
238
|
+
return json.dumps(detail, default=str)
|
|
239
|
+
if isinstance(detail, (list, tuple)):
|
|
240
|
+
return "; ".join(_detail_text(item) for item in detail)
|
|
241
|
+
return str(detail)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _protocol_error(path: str, status: int, message: str) -> JSONResponse:
|
|
245
|
+
"""Render one error message in the envelope the request's protocol owns."""
|
|
246
|
+
if path.startswith(OLLAMA_PREFIX):
|
|
247
|
+
return JSONResponse(status_code=status, content={"error": message})
|
|
248
|
+
if path.startswith(NATIVE_PREFIX):
|
|
249
|
+
return JSONResponse(status_code=status, content={"detail": message})
|
|
250
|
+
return _error(status, message, openai_error_type(status))
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
_bearer = HTTPBearer(auto_error=False)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def is_loopback_host(host: str) -> bool:
|
|
257
|
+
if host.lower() == "localhost":
|
|
258
|
+
return True
|
|
259
|
+
try:
|
|
260
|
+
address = ipaddress.ip_address(host)
|
|
261
|
+
if isinstance(address, ipaddress.IPv6Address) and address.ipv4_mapped is not None:
|
|
262
|
+
address = address.ipv4_mapped
|
|
263
|
+
return address.is_loopback
|
|
264
|
+
except ValueError:
|
|
265
|
+
return False
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def _origin(value: str) -> tuple[str, str, int] | None:
|
|
269
|
+
try:
|
|
270
|
+
parsed = urlsplit(value)
|
|
271
|
+
if (
|
|
272
|
+
parsed.scheme not in {"http", "https"} or not parsed.hostname
|
|
273
|
+
or parsed.username is not None or parsed.password is not None
|
|
274
|
+
or parsed.path or parsed.query or parsed.fragment
|
|
275
|
+
or any(char.isspace() for char in value) or "\\" in value
|
|
276
|
+
):
|
|
277
|
+
return None
|
|
278
|
+
port = parsed.port if parsed.port is not None else (443 if parsed.scheme == "https" else 80)
|
|
279
|
+
return parsed.scheme, parsed.hostname.lower(), port
|
|
280
|
+
except ValueError:
|
|
281
|
+
return None
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def _local_host_ok(conn: Request | WebSocket) -> bool:
|
|
285
|
+
client = conn.scope.get("client")
|
|
286
|
+
if client:
|
|
287
|
+
try:
|
|
288
|
+
ipaddress.ip_address(client[0])
|
|
289
|
+
except ValueError:
|
|
290
|
+
pass # In-process ASGI transports can use non-IP client labels.
|
|
291
|
+
else:
|
|
292
|
+
if not is_loopback_host(client[0]):
|
|
293
|
+
return False
|
|
294
|
+
hosts = conn.headers.getlist("host")
|
|
295
|
+
target = _origin("http://" + hosts[0]) if len(hosts) == 1 else None
|
|
296
|
+
if target is None:
|
|
297
|
+
return False
|
|
298
|
+
server = conn.scope.get("server")
|
|
299
|
+
return is_loopback_host(target[1]) or bool(server and target[1] == server[0].lower())
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def _check_bearer(headers: Headers, expected: str) -> bool:
|
|
303
|
+
"""Return True iff a correct ``Authorization: Bearer <expected>`` header is present."""
|
|
304
|
+
auth = headers.get("authorization") or headers.get("Authorization")
|
|
305
|
+
if not auth:
|
|
306
|
+
return False
|
|
307
|
+
scheme, _, token = auth.partition(" ")
|
|
308
|
+
return scheme.lower() == "bearer" and hmac.compare_digest(token.encode(), expected.encode())
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def _require_auth(request: Request = None, # pyright: ignore[reportArgumentType] # FastAPI injects Request/WebSocket by type; None default is a sentinel, not a real argument
|
|
312
|
+
websocket=None): # pyright: ignore[reportMissingParameterType] # FastAPI special-cases bare WebSocket/Request injection; an explicit `WebSocket | None` annotation makes it build a request field and raises at app-construction time
|
|
313
|
+
"""Bearer-token auth gate for HTTP routes.
|
|
314
|
+
|
|
315
|
+
Accepts either a ``Request`` or a ``WebSocket`` — FastAPI resolves the
|
|
316
|
+
non-None one based on the route type. On WebSocket connections we can't
|
|
317
|
+
raise ``HTTPException(401)`` (the handshake hasn't completed), so the
|
|
318
|
+
dep returns silently and the handler uses ``ws_auth_ok()`` + ``close(1008)``
|
|
319
|
+
before accepting the connection.
|
|
320
|
+
"""
|
|
321
|
+
conn = request if request is not None else websocket
|
|
322
|
+
if conn is None:
|
|
323
|
+
return
|
|
324
|
+
expected = getattr(conn.app.state, "api_key", None)
|
|
325
|
+
if not expected:
|
|
326
|
+
if request is not None and not _local_host_ok(request):
|
|
327
|
+
raise HTTPException(403, "Untrusted host; configure an API key for remote access")
|
|
328
|
+
return
|
|
329
|
+
if request is None:
|
|
330
|
+
# WS path: handler calls ws_auth_ok() before websocket.accept().
|
|
331
|
+
return
|
|
332
|
+
if not _check_bearer(request.headers, expected):
|
|
333
|
+
raise HTTPException(
|
|
334
|
+
status_code=401,
|
|
335
|
+
detail={"message": "Invalid API key", "type": "invalid_request_error",
|
|
336
|
+
"param": None, "code": 401},
|
|
337
|
+
)
|
|
338
|
+
return
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def _browser_origin_ok(conn: Request | WebSocket) -> bool:
|
|
342
|
+
origins = conn.headers.getlist("origin")
|
|
343
|
+
if origins:
|
|
344
|
+
origin = _origin(origins[0]) if len(origins) == 1 else None
|
|
345
|
+
hosts = conn.headers.getlist("host")
|
|
346
|
+
scheme = "https" if conn.scope["scheme"] in {"https", "wss"} else "http"
|
|
347
|
+
target = _origin(scheme + "://" + hosts[0]) if len(hosts) == 1 else None
|
|
348
|
+
allowed = conn.app.state.ws_origins
|
|
349
|
+
if origin is None or (origin != target and origin not in allowed):
|
|
350
|
+
return False
|
|
351
|
+
return True
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def ws_auth_ok(websocket: WebSocket) -> bool:
|
|
355
|
+
"""Validate browser Origin and bearer auth before accepting a WebSocket."""
|
|
356
|
+
query = parse_qsl(websocket.scope.get("query_string", b"").decode("latin-1"), keep_blank_values=True)
|
|
357
|
+
tokens = [value for name, value in query if name == "token"]
|
|
358
|
+
if tokens:
|
|
359
|
+
websocket.scope["query_string"] = urlencode([(name, value) for name, value in query if name != "token"]).encode()
|
|
360
|
+
expected = getattr(websocket.app.state, "api_key", None)
|
|
361
|
+
if not expected and not _local_host_ok(websocket):
|
|
362
|
+
return False
|
|
363
|
+
if not _browser_origin_ok(websocket):
|
|
364
|
+
return False
|
|
365
|
+
if not expected:
|
|
366
|
+
return True
|
|
367
|
+
if _check_bearer(websocket.headers, expected):
|
|
368
|
+
return True
|
|
369
|
+
credentials = [protocol.removeprefix("drowse.auth.") for protocol in websocket.scope.get("subprotocols", [])
|
|
370
|
+
if protocol.startswith("drowse.auth.")]
|
|
371
|
+
if credentials:
|
|
372
|
+
encoded = base64.urlsafe_b64encode(expected.encode()).rstrip(b"=")
|
|
373
|
+
return len(credentials) == 1 and hmac.compare_digest(credentials[0].encode(), encoded)
|
|
374
|
+
return len(tokens) == 1 and hmac.compare_digest(tokens[0].encode(), expected.encode())
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
class _HttpSecurityMiddleware:
|
|
378
|
+
def __init__(self, app: ASGIApp, *, max_request_bytes: int) -> None:
|
|
379
|
+
self.app = app
|
|
380
|
+
self.max_request_bytes = max_request_bytes
|
|
381
|
+
|
|
382
|
+
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
|
383
|
+
if scope["type"] != "http":
|
|
384
|
+
await self.app(scope, receive, send)
|
|
385
|
+
return
|
|
386
|
+
request = Request(scope)
|
|
387
|
+
path = scope["path"]
|
|
388
|
+
private = path.startswith((NATIVE_PREFIX, OLLAMA_PREFIX, "/v1/"))
|
|
389
|
+
|
|
390
|
+
async def secure_send(message: Message) -> None:
|
|
391
|
+
if message["type"] == "http.response.start":
|
|
392
|
+
headers = MutableHeaders(scope=message)
|
|
393
|
+
headers["X-Content-Type-Options"] = "nosniff"
|
|
394
|
+
headers["X-Frame-Options"] = "DENY"
|
|
395
|
+
headers["Referrer-Policy"] = "no-referrer"
|
|
396
|
+
headers["Permissions-Policy"] = "camera=(), microphone=(), geolocation=(), payment=(), usb=()"
|
|
397
|
+
headers["Content-Security-Policy"] = (
|
|
398
|
+
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; "
|
|
399
|
+
"img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self' https://www.neuronpedia.org https://drowse.ai/api/contact; "
|
|
400
|
+
"frame-ancestors 'none'; base-uri 'none'; object-src 'none'; form-action 'self'"
|
|
401
|
+
)
|
|
402
|
+
if private:
|
|
403
|
+
headers["Cache-Control"] = "no-store"
|
|
404
|
+
await send(message)
|
|
405
|
+
|
|
406
|
+
if private:
|
|
407
|
+
try:
|
|
408
|
+
_require_auth(request)
|
|
409
|
+
except HTTPException as exc:
|
|
410
|
+
handler = request.app.exception_handlers[StarletteHTTPException]
|
|
411
|
+
response = await handler(request, exc)
|
|
412
|
+
await response(scope, receive, secure_send)
|
|
413
|
+
return
|
|
414
|
+
try:
|
|
415
|
+
check_origin = private or scope["method"] not in {"GET", "HEAD", "OPTIONS"}
|
|
416
|
+
if check_origin and scope["method"] != "OPTIONS" and not _browser_origin_ok(request):
|
|
417
|
+
raise HTTPException(403, "Untrusted request origin")
|
|
418
|
+
lengths = request.headers.getlist("content-length")
|
|
419
|
+
if lengths:
|
|
420
|
+
if len(lengths) != 1 or not lengths[0].isascii() or not lengths[0].isdigit():
|
|
421
|
+
raise HTTPException(400, "Invalid Content-Length")
|
|
422
|
+
length = lengths[0].lstrip("0") or "0"
|
|
423
|
+
if len(length) > len(str(self.max_request_bytes)) or int(length) > self.max_request_bytes:
|
|
424
|
+
raise HTTPException(413, "Request body too large")
|
|
425
|
+
except HTTPException as exc:
|
|
426
|
+
response = _protocol_error(path, exc.status_code, _detail_text(exc.detail))
|
|
427
|
+
await response(scope, receive, secure_send)
|
|
428
|
+
return
|
|
429
|
+
|
|
430
|
+
received = 0
|
|
431
|
+
|
|
432
|
+
async def limited_receive() -> Message:
|
|
433
|
+
nonlocal received
|
|
434
|
+
message = await receive()
|
|
435
|
+
if message["type"] == "http.request":
|
|
436
|
+
received += len(message.get("body", b""))
|
|
437
|
+
if received > self.max_request_bytes:
|
|
438
|
+
raise HTTPException(413, "Request body too large")
|
|
439
|
+
return message
|
|
440
|
+
|
|
441
|
+
await self.app(scope, limited_receive, secure_send)
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def _sampling_kwargs(
|
|
445
|
+
req: _SamplingBase, default_steering: "Steering | None", session: DrowseSession,
|
|
446
|
+
) -> dict[str, Any]:
|
|
447
|
+
"""Build the kwargs dict passed to session.generate / generate_stream.
|
|
448
|
+
|
|
449
|
+
Returns ``sampling=SamplingConfig(...)`` + ``steering=Steering(...)``
|
|
450
|
+
/ None + ``thinking=`` + ``stateless=True``. The server never mutates
|
|
451
|
+
``session.config``.
|
|
452
|
+
|
|
453
|
+
Composes ``req.steering`` (expression string) over
|
|
454
|
+
``default_steering``: per-request keys override defaults. ``thinking``
|
|
455
|
+
is the native request override; ``None`` triggers
|
|
456
|
+
``supports_thinking`` auto-detect inside ``_generate_core``.
|
|
457
|
+
"""
|
|
458
|
+
# ``stop`` (str or list) and the two OpenAI ``logprobs`` shapes (chat's
|
|
459
|
+
# bool + ``top_logprobs`` count, completions' bare int) are normalized
|
|
460
|
+
# inside the shared constructor.
|
|
461
|
+
sc = build_sampling_config(
|
|
462
|
+
temperature=req.temperature,
|
|
463
|
+
top_p=req.top_p,
|
|
464
|
+
max_tokens=req.max_tokens,
|
|
465
|
+
seed=req.seed,
|
|
466
|
+
stop=req.stop,
|
|
467
|
+
logit_bias=req.logit_bias,
|
|
468
|
+
presence_penalty=req.presence_penalty,
|
|
469
|
+
frequency_penalty=req.frequency_penalty,
|
|
470
|
+
logprobs=req.logprobs,
|
|
471
|
+
top_logprobs=req.top_logprobs,
|
|
472
|
+
)
|
|
473
|
+
validate_logit_bias(sc, session)
|
|
474
|
+
|
|
475
|
+
steering = req.to_steering(default_steering)
|
|
476
|
+
|
|
477
|
+
thinking_kwarg: bool | None = req.thinking
|
|
478
|
+
if thinking_kwarg is None and steering is not None and steering.thinking is not None:
|
|
479
|
+
thinking_kwarg = steering.thinking
|
|
480
|
+
|
|
481
|
+
return {
|
|
482
|
+
"sampling": sc,
|
|
483
|
+
"steering": steering,
|
|
484
|
+
"thinking": thinking_kwarg,
|
|
485
|
+
"stateless": True,
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
def _token_bytes(text: str) -> list[int]:
|
|
490
|
+
try:
|
|
491
|
+
return list(text.encode("utf-8"))
|
|
492
|
+
except Exception:
|
|
493
|
+
return []
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def _render_logprobs_chat(result: GenerationResult, session: DrowseSession) -> dict[str, Any] | None:
|
|
497
|
+
if result.logprobs is None:
|
|
498
|
+
return None
|
|
499
|
+
tok = session.tokenizer
|
|
500
|
+
content = []
|
|
501
|
+
# Inner ``top`` is now ``list[TokenAlt]`` (id/text/logprob triples
|
|
502
|
+
# decoded by the engine at top-K capture time); the previous
|
|
503
|
+
# ``list[tuple[int, float]]`` pair shape is retired, so we no longer
|
|
504
|
+
# re-tokenize the alt ids here. The
|
|
505
|
+
# chosen-token text still goes through ``tok.decode`` because the
|
|
506
|
+
# engine emits its id via ``result.tokens`` without the streaming
|
|
507
|
+
# text representation alongside.
|
|
508
|
+
for tid, lp, top in result.logprobs:
|
|
509
|
+
tok_str: str = tok.decode([tid]) # pyright: ignore[reportAssignmentType] # transformers stub returns str | list[str] but single-list input always yields str
|
|
510
|
+
content.append({
|
|
511
|
+
"token": tok_str,
|
|
512
|
+
"logprob": lp,
|
|
513
|
+
"bytes": _token_bytes(tok_str),
|
|
514
|
+
"top_logprobs": [
|
|
515
|
+
{"token": alt.text, "logprob": alt.logprob,
|
|
516
|
+
"bytes": _token_bytes(alt.text)}
|
|
517
|
+
for alt in top
|
|
518
|
+
],
|
|
519
|
+
})
|
|
520
|
+
return {"content": content}
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
def _render_logprobs_completions(result: GenerationResult, session: DrowseSession) -> dict[str, Any] | None:
|
|
524
|
+
"""OpenAI /v1/completions logprobs shape (flat, token-parallel arrays).
|
|
525
|
+
|
|
526
|
+
https://platform.openai.com/docs/api-reference/completions/object#completions/object-logprobs
|
|
527
|
+
|
|
528
|
+
Inner ``top`` is ``list[TokenAlt]`` after the logit-alternative pass — alt
|
|
529
|
+
text comes off the dataclass rather than a redundant tokenizer
|
|
530
|
+
decode.
|
|
531
|
+
"""
|
|
532
|
+
if result.logprobs is None:
|
|
533
|
+
return None
|
|
534
|
+
tok = session.tokenizer
|
|
535
|
+
tokens: list[str] = []
|
|
536
|
+
token_logprobs: list[float | None] = []
|
|
537
|
+
top_logprobs: list[dict[str, float]] = []
|
|
538
|
+
text_offset: list[int] = []
|
|
539
|
+
offset = 0
|
|
540
|
+
for tid, lp, top in result.logprobs:
|
|
541
|
+
tok_str: str = tok.decode([tid]) # pyright: ignore[reportAssignmentType] # transformers stub returns str | list[str] but single-list input always yields str
|
|
542
|
+
tokens.append(tok_str)
|
|
543
|
+
token_logprobs.append(lp)
|
|
544
|
+
top_logprobs.append({alt.text: alt.logprob for alt in top})
|
|
545
|
+
text_offset.append(offset)
|
|
546
|
+
offset += len(tok_str)
|
|
547
|
+
return {
|
|
548
|
+
"tokens": tokens,
|
|
549
|
+
"token_logprobs": token_logprobs,
|
|
550
|
+
"top_logprobs": top_logprobs,
|
|
551
|
+
"text_offset": text_offset,
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
async def _stream_generation(
|
|
556
|
+
session: DrowseSession,
|
|
557
|
+
stream_factory: Callable[[], GenerationStream], rid: str, model_id: str, object_type: str,
|
|
558
|
+
format_delta: Callable[[Any], dict[str, Any]], empty_delta: dict[str, Any],
|
|
559
|
+
include_usage: bool = False, role_delta: bool = False,
|
|
560
|
+
request: Request | None = None,
|
|
561
|
+
):
|
|
562
|
+
"""Shared SSE generator for chat and completion streaming.
|
|
563
|
+
|
|
564
|
+
Serializes against other requests via ``session.lock`` for the full
|
|
565
|
+
stream lifetime (streams inherit queue semantics rather than 409).
|
|
566
|
+
Per-request sampling overrides are carried in the iterator's own
|
|
567
|
+
``sampling=`` kwarg (bound at caller site) — no session.config rebind.
|
|
568
|
+
|
|
569
|
+
The inner ``stream_iter`` is always ``.close()``d in a ``finally`` so the
|
|
570
|
+
engine's worker-thread teardown (stop-flag + join) fires deterministically
|
|
571
|
+
on early exit rather than waiting on GC. When a ``request`` is wired in,
|
|
572
|
+
a periodic ``is_disconnected()`` check stops generation early once the
|
|
573
|
+
client is gone, so the GPU isn't spent on a stream nobody reads.
|
|
574
|
+
"""
|
|
575
|
+
created_ts = int(time.time())
|
|
576
|
+
|
|
577
|
+
def _error_frames(status: int, message: str) -> list[str]:
|
|
578
|
+
"""One in-band error frame plus the terminating ``[DONE]`` sentinel.
|
|
579
|
+
|
|
580
|
+
``openai-python``'s ``Stream`` and LangChain's ``ChatOpenAI`` treat an
|
|
581
|
+
SSE body that ends without ``[DONE]`` the way ollama-python treats a
|
|
582
|
+
missing terminating ``done`` frame — they wait for it. The Ollama
|
|
583
|
+
NDJSON path already terminates after an in-band error chunk; this is
|
|
584
|
+
the same contract on the SSE side.
|
|
585
|
+
"""
|
|
586
|
+
err = {
|
|
587
|
+
"error": {
|
|
588
|
+
"message": message,
|
|
589
|
+
"type": openai_error_type(status),
|
|
590
|
+
"code": status,
|
|
591
|
+
},
|
|
592
|
+
}
|
|
593
|
+
return [f"data: {json.dumps(err)}\n\n", "data: [DONE]\n\n"]
|
|
594
|
+
|
|
595
|
+
async with acquire_session_lock(session) as acquired:
|
|
596
|
+
if not acquired:
|
|
597
|
+
for frame in _error_frames(503, "Server busy"):
|
|
598
|
+
yield frame
|
|
599
|
+
return
|
|
600
|
+
|
|
601
|
+
if role_delta:
|
|
602
|
+
chunk = {
|
|
603
|
+
"id": rid, "object": object_type, "created": created_ts,
|
|
604
|
+
"model": model_id,
|
|
605
|
+
"choices": [{"index": 0, "delta": {"role": "assistant"}, "finish_reason": None}],
|
|
606
|
+
}
|
|
607
|
+
yield f"data: {json.dumps(chunk)}\n\n"
|
|
608
|
+
stream_iter = None
|
|
609
|
+
try:
|
|
610
|
+
stream_iter = stream_factory()
|
|
611
|
+
async for event in stream_events(stream_iter):
|
|
612
|
+
# Bail out if the client has hung up — close the inner
|
|
613
|
+
# generator (handled in ``finally``) and stop spending the
|
|
614
|
+
# GPU on tokens nobody is reading.
|
|
615
|
+
if request is not None and await request.is_disconnected():
|
|
616
|
+
return
|
|
617
|
+
choice: dict[str, Any] = {
|
|
618
|
+
"index": 0, **format_delta(event), "finish_reason": None,
|
|
619
|
+
}
|
|
620
|
+
# Per-token manifold readings ride under a vendor-
|
|
621
|
+
# prefixed extension on the choice so OpenAI clients
|
|
622
|
+
# that don't read the field stay unaffected. Populated
|
|
623
|
+
# only when at least one manifold probe is attached
|
|
624
|
+
# and ``live_scores`` is True on the stream.
|
|
625
|
+
mf_token = probe_token_readings(event)
|
|
626
|
+
if mf_token is not None:
|
|
627
|
+
choice["x-drowse-probe-readings"] = mf_token
|
|
628
|
+
chunk = {
|
|
629
|
+
"id": rid,
|
|
630
|
+
"object": object_type,
|
|
631
|
+
"created": created_ts,
|
|
632
|
+
"model": model_id,
|
|
633
|
+
"choices": [choice],
|
|
634
|
+
}
|
|
635
|
+
yield f"data: {json.dumps(chunk)}\n\n"
|
|
636
|
+
except ConcurrentGenerationError:
|
|
637
|
+
for frame in _error_frames(409, "Generation already in progress"):
|
|
638
|
+
yield frame
|
|
639
|
+
return
|
|
640
|
+
except DrowseError as e:
|
|
641
|
+
status, msg = e.user_message()
|
|
642
|
+
for frame in _error_frames(status, msg):
|
|
643
|
+
yield frame
|
|
644
|
+
return
|
|
645
|
+
finally:
|
|
646
|
+
# Deterministically tear down the engine worker thread (stop-flag
|
|
647
|
+
# + join) on every exit — normal completion (no-op on an exhausted
|
|
648
|
+
# generator), an in-band error, or an early client-disconnect
|
|
649
|
+
# ``return`` — rather than leaving it to GC.
|
|
650
|
+
if stream_iter is not None:
|
|
651
|
+
await run_in_thread(stream_iter.close)
|
|
652
|
+
|
|
653
|
+
assert stream_iter is not None
|
|
654
|
+
last_result = stream_iter.result
|
|
655
|
+
finish_reason, usage, mf_agg = stream_finalizer(session, last_result)
|
|
656
|
+
final_choice: dict[str, Any] = {
|
|
657
|
+
"index": 0, **empty_delta, "finish_reason": finish_reason,
|
|
658
|
+
}
|
|
659
|
+
if mf_agg:
|
|
660
|
+
final_choice["x-drowse-probe-readings"] = mf_agg
|
|
661
|
+
final = {
|
|
662
|
+
"id": rid,
|
|
663
|
+
"object": object_type,
|
|
664
|
+
"created": created_ts,
|
|
665
|
+
"model": model_id,
|
|
666
|
+
"choices": [final_choice],
|
|
667
|
+
}
|
|
668
|
+
yield f"data: {json.dumps(final)}\n\n"
|
|
669
|
+
|
|
670
|
+
if include_usage and usage is not None:
|
|
671
|
+
usage_chunk = {
|
|
672
|
+
"id": rid, "object": object_type, "created": created_ts,
|
|
673
|
+
"model": model_id, "choices": [],
|
|
674
|
+
"usage": usage,
|
|
675
|
+
}
|
|
676
|
+
yield f"data: {json.dumps(usage_chunk)}\n\n"
|
|
677
|
+
|
|
678
|
+
yield "data: [DONE]\n\n"
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
# ---------------------------------------------------------------------------
|
|
682
|
+
# App factory
|
|
683
|
+
# ---------------------------------------------------------------------------
|
|
684
|
+
|
|
685
|
+
def create_app(session: DrowseSession,
|
|
686
|
+
default_steering: "Steering | None" = None,
|
|
687
|
+
cors_origins: list[str] | None = None,
|
|
688
|
+
api_key: str | None = None,
|
|
689
|
+
*,
|
|
690
|
+
web: bool = False,
|
|
691
|
+
max_request_bytes: int | None = None) -> FastAPI:
|
|
692
|
+
if max_request_bytes is None:
|
|
693
|
+
max_request_bytes = int(os.environ.get("DROWSE_MAX_REQUEST_BYTES", DEFAULT_MAX_REQUEST_BYTES))
|
|
694
|
+
if type(max_request_bytes) is not int or max_request_bytes <= 0:
|
|
695
|
+
raise ValueError("max_request_bytes must be a positive integer")
|
|
696
|
+
app = FastAPI(
|
|
697
|
+
title="drowse",
|
|
698
|
+
description="OpenAI-compatible API with activation steering",
|
|
699
|
+
dependencies=[Depends(_require_auth)],
|
|
700
|
+
)
|
|
701
|
+
app.state.session = session
|
|
702
|
+
app.state.default_steering = default_steering
|
|
703
|
+
app.state.created_ts = int(time.time())
|
|
704
|
+
app.state.api_key = api_key if api_key is not None else os.environ.get("DROWSE_API_KEY")
|
|
705
|
+
app.state.ws_origins = {origin for value in (cors_origins or []) if (origin := _origin(value)) is not None}
|
|
706
|
+
app.add_middleware(_HttpSecurityMiddleware, max_request_bytes=max_request_bytes)
|
|
707
|
+
# Generation serialization lives on ``session.lock`` (asyncio.Lock)
|
|
708
|
+
# so both the OpenAI and Ollama route families share a single FIFO
|
|
709
|
+
# queue. Requests wait rather than 409 on contention.
|
|
710
|
+
|
|
711
|
+
if cors_origins:
|
|
712
|
+
app.add_middleware(
|
|
713
|
+
CORSMiddleware,
|
|
714
|
+
allow_origins=cors_origins,
|
|
715
|
+
allow_methods=["*"],
|
|
716
|
+
allow_headers=["*"],
|
|
717
|
+
)
|
|
718
|
+
|
|
719
|
+
@app.exception_handler(DrowseError)
|
|
720
|
+
async def _on_drowse_error(request: Request, exc: DrowseError):
|
|
721
|
+
if exc.__cause__ is not None:
|
|
722
|
+
logging.getLogger("drowse.api").error("Request failed", exc_info=exc)
|
|
723
|
+
status, msg = exc.user_message()
|
|
724
|
+
return _protocol_error(request.url.path, status, msg)
|
|
725
|
+
|
|
726
|
+
@app.exception_handler(RequestValidationError)
|
|
727
|
+
async def _on_validation_error(request: Request, exc: RequestValidationError):
|
|
728
|
+
errs = exc.errors()
|
|
729
|
+
first = errs[0] if errs else {}
|
|
730
|
+
loc = first.get("loc", ())
|
|
731
|
+
param = ".".join(str(p) for p in loc[1:]) if len(loc) > 1 else (str(loc[0]) if loc else None)
|
|
732
|
+
msg = first.get("msg", "Invalid request")
|
|
733
|
+
path = request.url.path
|
|
734
|
+
if path.startswith(NATIVE_PREFIX) or path.startswith(OLLAMA_PREFIX):
|
|
735
|
+
# The native tree and the Ollama shim each own an envelope; a
|
|
736
|
+
# body-validation failure has to speak the same one the rest of
|
|
737
|
+
# that protocol's failures do.
|
|
738
|
+
return _protocol_error(
|
|
739
|
+
path, 400, f"{param}: {msg}" if param else msg,
|
|
740
|
+
)
|
|
741
|
+
return _error(400, msg, "invalid_request_error", param=param)
|
|
742
|
+
|
|
743
|
+
@app.exception_handler(StarletteHTTPException)
|
|
744
|
+
async def _on_http_exception(request: Request, exc: StarletteHTTPException):
|
|
745
|
+
"""Normalize the native tree's ``HTTPException``s to one envelope.
|
|
746
|
+
|
|
747
|
+
Almost every native route raises a bare ``HTTPException``, which
|
|
748
|
+
Starlette renders as ``{"detail": …}`` — but ``detail`` could be a
|
|
749
|
+
string, the auth dependency's dict, or a list of pydantic error dicts,
|
|
750
|
+
so a client had to guess. On ``/drowse/v1/*`` it is always a string;
|
|
751
|
+
every other prefix keeps FastAPI's default rendering.
|
|
752
|
+
"""
|
|
753
|
+
if isinstance(exc.__cause__, DrowseError):
|
|
754
|
+
_status, detail = exc.__cause__.user_message()
|
|
755
|
+
if exc.__cause__.__cause__ is not None:
|
|
756
|
+
logging.getLogger("drowse.api").error("Request failed", exc_info=exc.__cause__)
|
|
757
|
+
return _protocol_error(request.url.path, exc.status_code, detail)
|
|
758
|
+
if isinstance(exc.__cause__, OSError):
|
|
759
|
+
logging.getLogger("drowse.api").error("Filesystem request failed", exc_info=exc.__cause__)
|
|
760
|
+
detail = {404: "Requested artifact not found", 409: "An artifact already exists at the destination"}.get(
|
|
761
|
+
exc.status_code, "Filesystem operation failed. Check the server log for details.",
|
|
762
|
+
)
|
|
763
|
+
return _protocol_error(request.url.path, exc.status_code, detail)
|
|
764
|
+
response = await http_exception_handler(request, exc)
|
|
765
|
+
detail = cast(object, exc.detail)
|
|
766
|
+
if (
|
|
767
|
+
request.url.path.startswith(NATIVE_PREFIX)
|
|
768
|
+
and not isinstance(detail, str)
|
|
769
|
+
# A bodiless status (204 / 304) comes back as a bare ``Response``
|
|
770
|
+
# from the default handler — leave those alone.
|
|
771
|
+
and isinstance(response, JSONResponse)
|
|
772
|
+
):
|
|
773
|
+
return JSONResponse(
|
|
774
|
+
status_code=exc.status_code,
|
|
775
|
+
content={"detail": _detail_text(detail)},
|
|
776
|
+
headers=getattr(exc, "headers", None),
|
|
777
|
+
)
|
|
778
|
+
return response
|
|
779
|
+
|
|
780
|
+
_register_routes(app)
|
|
781
|
+
|
|
782
|
+
# Mount Ollama-compatible /api/* routes alongside OpenAI routes so any
|
|
783
|
+
# Ollama client (Open WebUI, Enchanted, ollama-python, etc.) talks to
|
|
784
|
+
# drowse as a drop-in replacement.
|
|
785
|
+
from drowse.server.ollama import register_ollama_routes
|
|
786
|
+
register_ollama_routes(app)
|
|
787
|
+
|
|
788
|
+
from drowse.server.native_routes import register_drowse_routes
|
|
789
|
+
register_drowse_routes(app)
|
|
790
|
+
|
|
791
|
+
# Mount the Svelte+Vite SPA dashboard last so its catch-all route
|
|
792
|
+
# doesn't shadow any of the API routes registered above. CLI
|
|
793
|
+
# default-on (``drowse serve``); ``--no-web`` opts out for
|
|
794
|
+
# production / proxied deployments where ``/`` already belongs to
|
|
795
|
+
# something else. Library callers using ``create_app`` directly
|
|
796
|
+
# default-off so embedded API surfaces don't accidentally pick up
|
|
797
|
+
# the dashboard.
|
|
798
|
+
if web:
|
|
799
|
+
from drowse.web import register_web_routes
|
|
800
|
+
|
|
801
|
+
register_web_routes(app)
|
|
802
|
+
|
|
803
|
+
return app
|
|
804
|
+
|
|
805
|
+
|
|
806
|
+
def _openai_known_model_names(session: DrowseSession) -> set[str]:
|
|
807
|
+
"""Names accepted for OpenAI routes in strict mode.
|
|
808
|
+
|
|
809
|
+
Includes the HF id plus any Ollama-style aliases (`<family>:<size>`)
|
|
810
|
+
— the OpenAI catalogue is a superset so clients hitting either
|
|
811
|
+
protocol with the same name keep working.
|
|
812
|
+
"""
|
|
813
|
+
from drowse.server.model_names import known_model_names
|
|
814
|
+
|
|
815
|
+
return known_model_names(session)
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
def _check_openai_model_strict(session: DrowseSession, name: str | None) -> None:
|
|
819
|
+
if not strict_model_enabled():
|
|
820
|
+
return
|
|
821
|
+
if not name:
|
|
822
|
+
return
|
|
823
|
+
if name.lower() not in _openai_known_model_names(session):
|
|
824
|
+
raise HTTPException(
|
|
825
|
+
status_code=404,
|
|
826
|
+
detail={
|
|
827
|
+
"message": f"Model '{name}' not found",
|
|
828
|
+
"type": "invalid_request_error",
|
|
829
|
+
"param": "model",
|
|
830
|
+
"code": 404,
|
|
831
|
+
},
|
|
832
|
+
)
|
|
833
|
+
|
|
834
|
+
|
|
835
|
+
def _register_routes(app: FastAPI) -> None:
|
|
836
|
+
session: DrowseSession = app.state.session
|
|
837
|
+
|
|
838
|
+
# -----------------------------------------------------------------------
|
|
839
|
+
# Models
|
|
840
|
+
# -----------------------------------------------------------------------
|
|
841
|
+
|
|
842
|
+
@app.get("/v1/models")
|
|
843
|
+
def list_models():
|
|
844
|
+
return {
|
|
845
|
+
"object": "list",
|
|
846
|
+
"data": [
|
|
847
|
+
{
|
|
848
|
+
"id": session.model_id,
|
|
849
|
+
"object": "model",
|
|
850
|
+
"created": app.state.created_ts,
|
|
851
|
+
"owned_by": "local",
|
|
852
|
+
}
|
|
853
|
+
],
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
@app.get("/v1/models/{model_id:path}")
|
|
857
|
+
def get_model(model_id: str):
|
|
858
|
+
if model_id != session.model_id:
|
|
859
|
+
raise HTTPException(404, f"Model '{model_id}' not found")
|
|
860
|
+
return {
|
|
861
|
+
"id": session.model_id,
|
|
862
|
+
"object": "model",
|
|
863
|
+
"created": app.state.created_ts,
|
|
864
|
+
"owned_by": "local",
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
# -----------------------------------------------------------------------
|
|
868
|
+
# Chat completions
|
|
869
|
+
# -----------------------------------------------------------------------
|
|
870
|
+
|
|
871
|
+
async def _run_blocking(req: _SamplingBase, prompt_or_messages: Any, *, raw: bool) -> Any:
|
|
872
|
+
gen_kwargs = _sampling_kwargs(req, app.state.default_steering, session)
|
|
873
|
+
# Bounded lock so a non-streaming request can't queue forever behind
|
|
874
|
+
# a stuck generation — it 503s like the streaming paths do. Returns
|
|
875
|
+
# a ``JSONResponse`` on timeout; callers return it verbatim.
|
|
876
|
+
async with acquire_session_lock(session) as acquired:
|
|
877
|
+
if not acquired:
|
|
878
|
+
return _error(503, "Server busy", "server_error")
|
|
879
|
+
return (await run_in_thread(session.generate, prompt_or_messages, raw=raw, **gen_kwargs)).first
|
|
880
|
+
|
|
881
|
+
@app.post("/v1/chat/completions")
|
|
882
|
+
async def chat_completions(req: ChatCompletionRequest, request: Request):
|
|
883
|
+
_check_openai_model_strict(session, req.model)
|
|
884
|
+
messages = [{"role": m.role, "content": m.content} for m in req.messages]
|
|
885
|
+
rid = _make_id()
|
|
886
|
+
model_id = session.model_id
|
|
887
|
+
gen_kwargs = _sampling_kwargs(req, app.state.default_steering, session)
|
|
888
|
+
|
|
889
|
+
if req.stream:
|
|
890
|
+
def _chat_delta(event: Any) -> dict[str, Any]:
|
|
891
|
+
d: dict[str, str] = {}
|
|
892
|
+
if event.thinking:
|
|
893
|
+
d["reasoning_content"] = event.text
|
|
894
|
+
else:
|
|
895
|
+
d["content"] = event.text
|
|
896
|
+
return {"delta": d}
|
|
897
|
+
|
|
898
|
+
def stream_factory() -> GenerationStream:
|
|
899
|
+
return session.generate_stream(
|
|
900
|
+
messages, live_scores=False, live_readouts=False, **gen_kwargs,
|
|
901
|
+
)
|
|
902
|
+
include_usage = bool(req.stream_options and req.stream_options.include_usage)
|
|
903
|
+
return ClosingStreamingResponse(
|
|
904
|
+
_stream_generation(session,
|
|
905
|
+
stream_factory, rid, model_id,
|
|
906
|
+
"chat.completion.chunk", _chat_delta, {"delta": {}},
|
|
907
|
+
include_usage=include_usage, role_delta=True,
|
|
908
|
+
request=request),
|
|
909
|
+
media_type="text/event-stream",
|
|
910
|
+
)
|
|
911
|
+
try:
|
|
912
|
+
result = await _run_blocking(req, messages, raw=False)
|
|
913
|
+
except ConcurrentGenerationError:
|
|
914
|
+
return _error(409, "Generation already in progress", "conflict")
|
|
915
|
+
if isinstance(result, JSONResponse): # bounded-lock timeout → 503
|
|
916
|
+
return result
|
|
917
|
+
|
|
918
|
+
chat_choice: dict[str, Any] = {
|
|
919
|
+
"index": 0,
|
|
920
|
+
"message": {"role": "assistant", "content": result.text},
|
|
921
|
+
"logprobs": _render_logprobs_chat(result, session),
|
|
922
|
+
"finish_reason": result.finish_reason,
|
|
923
|
+
}
|
|
924
|
+
mf_chat = probe_reading_aggregate(session, result)
|
|
925
|
+
if mf_chat:
|
|
926
|
+
chat_choice["x-drowse-probe-readings"] = mf_chat
|
|
927
|
+
body = {
|
|
928
|
+
"id": rid,
|
|
929
|
+
"object": "chat.completion",
|
|
930
|
+
"created": int(time.time()),
|
|
931
|
+
"model": model_id,
|
|
932
|
+
"choices": [chat_choice],
|
|
933
|
+
"usage": usage_dict(result),
|
|
934
|
+
}
|
|
935
|
+
return body
|
|
936
|
+
|
|
937
|
+
# -----------------------------------------------------------------------
|
|
938
|
+
# Text completions
|
|
939
|
+
# -----------------------------------------------------------------------
|
|
940
|
+
|
|
941
|
+
@app.post("/v1/completions")
|
|
942
|
+
async def completions(req: CompletionRequest, request: Request):
|
|
943
|
+
_check_openai_model_strict(session, req.model)
|
|
944
|
+
rid = _make_id()
|
|
945
|
+
model_id = session.model_id
|
|
946
|
+
gen_kwargs = _sampling_kwargs(req, app.state.default_steering, session)
|
|
947
|
+
|
|
948
|
+
if req.stream:
|
|
949
|
+
def stream_factory() -> GenerationStream:
|
|
950
|
+
return session.generate_stream(
|
|
951
|
+
req.prompt, raw=True, live_scores=False, live_readouts=False,
|
|
952
|
+
**gen_kwargs,
|
|
953
|
+
)
|
|
954
|
+
include_usage = bool(req.stream_options and req.stream_options.include_usage)
|
|
955
|
+
return ClosingStreamingResponse(
|
|
956
|
+
_stream_generation(session,
|
|
957
|
+
stream_factory, rid, model_id,
|
|
958
|
+
"text_completion", lambda e: {"text": e.text}, {"text": ""},
|
|
959
|
+
include_usage=include_usage, role_delta=False,
|
|
960
|
+
request=request),
|
|
961
|
+
media_type="text/event-stream",
|
|
962
|
+
)
|
|
963
|
+
try:
|
|
964
|
+
result = await _run_blocking(req, req.prompt, raw=True)
|
|
965
|
+
except ConcurrentGenerationError:
|
|
966
|
+
return _error(409, "Generation already in progress", "conflict")
|
|
967
|
+
if isinstance(result, JSONResponse): # bounded-lock timeout → 503
|
|
968
|
+
return result
|
|
969
|
+
|
|
970
|
+
completion_choice: dict[str, Any] = {
|
|
971
|
+
"index": 0,
|
|
972
|
+
"text": result.text,
|
|
973
|
+
"logprobs": _render_logprobs_completions(result, session),
|
|
974
|
+
"finish_reason": result.finish_reason,
|
|
975
|
+
}
|
|
976
|
+
mf_completion = probe_reading_aggregate(session, result)
|
|
977
|
+
if mf_completion:
|
|
978
|
+
completion_choice["x-drowse-probe-readings"] = mf_completion
|
|
979
|
+
body = {
|
|
980
|
+
"id": rid,
|
|
981
|
+
"object": "text_completion",
|
|
982
|
+
"created": int(time.time()),
|
|
983
|
+
"model": model_id,
|
|
984
|
+
"choices": [completion_choice],
|
|
985
|
+
"usage": usage_dict(result),
|
|
986
|
+
}
|
|
987
|
+
return body
|