diff --git a/frontend/src/lib/components/raw_apps/RawAppTemplatePicker.svelte b/frontend/src/lib/components/raw_apps/RawAppTemplatePicker.svelte index 8e6b481721..36da23ef7f 100644 --- a/frontend/src/lib/components/raw_apps/RawAppTemplatePicker.svelte +++ b/frontend/src/lib/components/raw_apps/RawAppTemplatePicker.svelte @@ -141,10 +141,14 @@ roles.current.permissioned && loadedRoles.length === 0 ) + // A lookup that failed is not an answer that there is nothing to pick, and an + // app naming this data table would be created against whatever the default + // turns out to be. + const rolesUnknown = $derived(rolesSettled && roles.current.failed) // Only the app that will name the data table is refused: with table creation // off nothing saves it, and an app that does not touch it is not this caller's // problem to be stopped over. - const blockedByRole = $derived(noUsableRole && tableCreationEnabled) + const blockedByRole = $derived((noUsableRole || rolesUnknown) && tableCreationEnabled) const accessSettled = $derived( hasNoDatatables || (rolesSettled && @@ -361,9 +365,9 @@ size="sm" class="w-40" /> - {#if noUsableRole} + {#if noUsableRole || rolesUnknown} - no role you can use + {rolesUnknown ? 'could not read its roles' : 'no role you can use'} {/if} {#if showRolePicker} diff --git a/frontend/src/lib/components/raw_apps/datatableUtils.svelte.ts b/frontend/src/lib/components/raw_apps/datatableUtils.svelte.ts index 2fcb438c24..59245bbdf9 100644 --- a/frontend/src/lib/components/raw_apps/datatableUtils.svelte.ts +++ b/frontend/src/lib/components/raw_apps/datatableUtils.svelte.ts @@ -57,12 +57,17 @@ export function createRolesResource( * there is nothing to pick — which is not the same as a permissioned one * this caller may run as nothing on. */ permissioned: boolean + /** The lookup itself failed, so neither of the above is an answer: an + * empty `roles` here means nothing was learned, not that there is + * nothing to pick. */ + failed: boolean roles: string[] defaultRole: string }> => { const empty = { datatable: datatableName || undefined, permissioned: false, + failed: false, roles: [], defaultRole: ADMIN_DATATABLE_ROLE } @@ -77,13 +82,14 @@ export function createRolesResource( } } catch (e) { console.error('Failed to load datatable roles:', e) - return empty + return { ...empty, failed: true } } }, { initialValue: { datatable: undefined, permissioned: false, + failed: false, roles: [], defaultRole: ADMIN_DATATABLE_ROLE } diff --git a/frontend/src/lib/components/workspaceSettings/ForkDatatableSection.svelte b/frontend/src/lib/components/workspaceSettings/ForkDatatableSection.svelte index 2ee61ad08b..8daa78ff15 100644 --- a/frontend/src/lib/components/workspaceSettings/ForkDatatableSection.svelte +++ b/frontend/src/lib/components/workspaceSettings/ForkDatatableSection.svelte @@ -58,10 +58,13 @@ workspace: ws, datatableName: dt.name }) - return { ...dt, permissioned: roles.enabled } + return { ...dt, permissioned: roles.enabled as boolean | undefined } } catch (e) { + // Not `false`: what the fork does with this data table is decided by + // the config, and saying "kept" for one the backend will drop loses + // it silently. console.error('Failed to read datatable permissions:', e) - return { ...dt, permissioned: false } + return { ...dt, permissioned: undefined } } }) ) @@ -213,7 +216,12 @@ items={[ { value: 'keep_original', - label: dt.permissioned ? 'Not shared (permissions enabled)' : 'Keep original' + label: + dt.permissioned === undefined + ? 'Keep original (permissions unknown)' + : dt.permissioned + ? 'Not shared (permissions enabled)' + : 'Keep original' }, { value: 'schema_only', label: 'Clone schema only' }, ...(!isCloudHosted() && $userStore?.is_admin diff --git a/system_prompts/auto-generated/prompts.ts b/system_prompts/auto-generated/prompts.ts index 262ecda2be..809e306f68 100644 --- a/system_prompts/auto-generated/prompts.ts +++ b/system_prompts/auto-generated/prompts.ts @@ -3052,7 +3052,7 @@ Import: \`import wmill\` # # Example: # wmill.datatable("main", role="operator") -def datatable(name: str = 'main', role: Optional[str] = None) -> DataTableClient +def datatable(name: str = 'main', *, role: Optional[str] = None) -> DataTableClient # Client for executing SQL queries against Windmill DataTables. class DataTableClient: diff --git a/system_prompts/auto-generated/sdks/datatable-python.md b/system_prompts/auto-generated/sdks/datatable-python.md index 2970d6d3b9..d227378fde 100644 --- a/system_prompts/auto-generated/sdks/datatable-python.md +++ b/system_prompts/auto-generated/sdks/datatable-python.md @@ -14,7 +14,7 @@ Import: `import wmill` # # Example: # wmill.datatable("main", role="operator") -def datatable(name: str = 'main', role: Optional[str] = None) -> DataTableClient +def datatable(name: str = 'main', *, role: Optional[str] = None) -> DataTableClient # Client for executing SQL queries against Windmill DataTables. class DataTableClient: diff --git a/system_prompts/generate.py b/system_prompts/generate.py index 6d99be1036..9ced329f65 100644 --- a/system_prompts/generate.py +++ b/system_prompts/generate.py @@ -1265,6 +1265,10 @@ def _format_py_params(node: ast.FunctionDef, skip_self: bool = False) -> str: if args.vararg.annotation: vararg_str += f": {ast.unparse(args.vararg.annotation)}" params.append(vararg_str) + elif args.kwonlyargs: + # The bare separator is part of the signature: without it the advertised + # call is positional, and an agent following it gets a TypeError. + params.append('*') for i, arg in enumerate(args.kwonlyargs): param_str = arg.arg