Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
A
aisbf
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexlab
aisbf
Commits
57c92dd0
Commit
57c92dd0
authored
May 10, 2026
by
Stefy Lanza (nextime / spora )
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix(studio): tighten partial capability fallback
parent
3aa1cfcc
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
58 additions
and
2 deletions
+58
-2
providers.py
aisbf/routes/dashboard/providers.py
+11
-1
studio.py
aisbf/studio.py
+7
-0
test_dashboard_studio.py
tests/routes/test_dashboard_studio.py
+29
-1
test_studio.py
tests/test_studio.py
+11
-0
No files found.
aisbf/routes/dashboard/providers.py
View file @
57c92dd0
...
...
@@ -256,6 +256,16 @@ async def dashboard_studio(request: Request):
if
auth_check
:
return
auth_check
current_user_id
=
request
.
session
.
get
(
"user_id"
)
scope
=
"admin"
if
request
.
session
.
get
(
"role"
)
==
"admin"
else
"user"
db
=
None
if
scope
==
"admin"
else
DatabaseRegistry
.
get_config_database
()
catalog
=
build_studio_catalog
(
scope
=
scope
,
owner_id
=
current_user_id
,
config
=
_config
,
db
=
db
,
)
return
_templates
.
TemplateResponse
(
request
=
request
,
name
=
"dashboard/studio.html"
,
...
...
@@ -263,7 +273,7 @@ async def dashboard_studio(request: Request):
"request"
:
request
,
"session"
:
request
.
session
,
"__version__"
:
__version__
,
"studio_bootstrap_json"
:
json
.
dumps
(
{}
),
"studio_bootstrap_json"
:
json
.
dumps
(
catalog
),
"studio_body_mode"
:
"wide"
,
},
)
...
...
aisbf/studio.py
View file @
57c92dd0
...
...
@@ -207,10 +207,13 @@ def merge_capabilities(
def
derive_aggregate_capabilities
(
capability_sets
:
Iterable
[
Optional
[
Iterable
[
str
]]])
->
StudioCapabilityMergeResult
:
capability_sets
=
list
(
capability_sets
)
normalized_sets
=
[
normalize_capabilities
(
capabilities
)
for
capabilities
in
capability_sets
if
capabilities
]
if
not
normalized_sets
:
return
StudioCapabilityMergeResult
(
capabilities
=
[],
partial_capabilities
=
[])
has_unknown_member
=
any
(
not
normalize_capabilities
(
capabilities
)
for
capabilities
in
capability_sets
)
aggregate
=
list
(
normalized_sets
[
0
])
partial
:
List
[
str
]
=
[]
for
capability_set
in
normalized_sets
[
1
:]:
...
...
@@ -218,6 +221,10 @@ def derive_aggregate_capabilities(capability_sets: Iterable[Optional[Iterable[st
aggregate
=
merged
.
capabilities
partial
=
_dedupe
([
*
partial
,
*
merged
.
partial_capabilities
,
*
[
capability
for
capability
in
capability_set
if
capability
not
in
aggregate
]])
if
has_unknown_member
:
partial
=
_dedupe
([
*
partial
,
*
aggregate
])
aggregate
=
[]
partial
=
[
capability
for
capability
in
partial
if
capability
not
in
aggregate
]
return
StudioCapabilityMergeResult
(
capabilities
=
aggregate
,
partial_capabilities
=
partial
)
...
...
tests/routes/test_dashboard_studio.py
View file @
57c92dd0
...
...
@@ -80,7 +80,35 @@ def test_dashboard_studio_renders_empty_diagnostics_contract_for_shell_boot():
assert
response
.
status_code
==
200
assert
'id="studio-diagnostics" data-empty-message="No diagnostics yet."'
in
response
.
text
assert
'<span data-i18n="studio.diagnostics_empty">No diagnostics yet.</span>'
in
response
.
text
assert
'<script id="studio-bootstrap" type="application/json">{}</script>'
in
response
.
text
def
test_dashboard_studio_bootstraps_initial_catalog_data
(
monkeypatch
):
client
=
TestClient
(
app
)
_login_as_admin
(
client
)
monkeypatch
.
setattr
(
dashboard_providers
,
"build_studio_catalog"
,
lambda
**
kwargs
:
{
"scope"
:
kwargs
[
"scope"
],
"owner_id"
:
kwargs
[
"owner_id"
],
"entries"
:
[
{
"id"
:
"rotation/creative-rotation"
,
"kind"
:
"rotation"
,
"label"
:
"Creative Rotation"
,
"capabilities"
:
[
"chat"
],
"partial_capabilities"
:
[
"vision"
],
}
],
},
)
response
=
client
.
get
(
"/dashboard/studio"
)
assert
response
.
status_code
==
200
assert
'"id": "rotation/creative-rotation"'
in
response
.
text
assert
'"partial_capabilities": ["vision"]'
in
response
.
text
def
test_dashboard_studio_catalog_returns_global_resources_for_admin
(
monkeypatch
):
...
...
tests/test_studio.py
View file @
57c92dd0
...
...
@@ -3,6 +3,7 @@ import pytest
from
aisbf.studio
import
(
StudioCapabilityResult
,
build_catalog_entry
,
derive_aggregate_capabilities
,
infer_model_capabilities
,
merge_capabilities
,
stamp_inferred_capabilities
,
...
...
@@ -234,3 +235,13 @@ def test_merge_capabilities_reports_partial_support_for_rotation_intersection():
assert
merged
.
capabilities
==
[
"chat"
,
"vision"
]
assert
merged
.
partial_capabilities
==
[
"image_generation"
]
def
test_derive_aggregate_capabilities_marks_missing_member_capabilities_as_uncertain
():
derived
=
derive_aggregate_capabilities
([
[
"chat"
,
"vision"
],
None
,
])
assert
derived
.
capabilities
==
[]
assert
derived
.
partial_capabilities
==
[
"chat"
,
"vision"
]
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment