Projects & Workspaces
A project is the anchor for everything you evaluate. It pins down the data schema you are working against, declares the categorical dimensions the platform slices your data and results by, and gives your datasets, systems under test (SUTs), and runs a common parent. Every workflow begins by creating a project or reusing the one you created earlier.
Choosing a schema and task type
A project is bound to a GDI schema — the shape of a single evaluation row.
Text projects use gdi_text_v1; image projects use gdi_image_v1.
Task-scoped schemas require you to also declare a task type, because the same schema serves several evaluation shapes:
gdi_text_v1→single_turn_llm(a single prompt/response LLM turn) orsingle_turn_rag(a retrieval-augmented turn that also carries retrieved context).gdi_image_v1→detection(object-detection rows with bounding boxes).
The task type must match the SUT you connect later, so pick it deliberately up front. If you omit it for a task-scoped schema, creation is rejected.
Creating a project
Project.create(...) provisions a new project. name, schema, and — for
task-scoped schemas — task_type are the required arguments; dimensions and
workspace_id are optional. Each Dimension names a categorical column
(name), the dataset column it reads from (column), and its allowed values.
The call returns a Project whose id you use everywhere downstream.
SDK
Example 1 — a text / single-turn LLM project:
import aip_sdk as aip
project = aip.Project.create(
name="My Project",
schema="gdi_text_v1",
task_type="single_turn_llm",
dimensions=[aip.Dimension(name="intent", column="intent", values=["a", "b"])],
workspace_id=ws.id,
)
Example 2 — an image / object-detection project:
detection_project = aip.Project.create(
name="Detection",
schema="gdi_image_v1",
task_type="detection",
dimensions=[aip.Dimension(name="object_class", column="object_class", values=["person", "vehicle"])],
workspace_id=ws.id,
)
Instead of hand-writing dimensions you can seed them from a built-in
template, which fills in a ready-made set of slices for a common use case.
Resolve a template into dimensions and pass them to Project.create(...):
dims = aip.templates.dimensions("customer_service_chatbot")
project = aip.Project.create(name="CS bot", schema="gdi_text_v1", task_type="single_turn_llm", dimensions=dims)
API
POST the same fields to the /projects endpoint:
curl -sS -X POST "$AIP_API_URL/projects" \
-H "Authorization: Bearer $AIP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "My Project",
"schema_name": "gdi_text_v1",
"task_type": "single_turn_llm",
"dimensions": [{"name": "intent", "column": "intent", "values": ["a", "b"]}]
}'
Get-or-create (idempotent)
In a workflow you rerun — a notebook, a CI job — you want the project created the
first time and reused every time after, without a "already exists" error.
Project.get_or_create(...) does exactly that: it looks for a project matching
your name and schema in the workspace, creates it if absent, and returns a
(project, created) tuple. The created boolean tells you which path was taken
— True on the first run, False when the existing project is reused. This is
the recommended entry point for the standard workflow. Supplying dimensions
also treats them as the desired configuration on reuse. If they differ, AIP
replaces the project dimensions and prepares a recovery draft for each previously
promoted dataset, either by creating one or marking the existing pending version stale.
The SDK logs an actionable warning naming drafts that need quality checks and promotion.
Reordering dimensions or their vocabulary does not trigger a write; omitting
dimensions leaves the existing configuration alone.
SDK
project, created = aip.Project.get_or_create(
name="OpenAI LLM Demo",
schema="gdi_text_v1",
task_type="single_turn_llm",
dimensions=[aip.Dimension(name="topic", column="topic", values=["general_knowledge"])],
workspace_id=ws.id,
)
The get-or-create shape carries the same task-type rule. A RAG project only
differs by its task_type, which must match the RAG SUT you connect in the next
step:
rag_project, created = aip.Project.get_or_create(
name="AnythingLLM RAG Demo",
schema="gdi_text_v1",
task_type="single_turn_rag",
dimensions=[aip.Dimension(name="topic", column="topic", values=["nist_ai_rmf"])],
workspace_id=ws.id,
)
Retrieving a project
Once a project exists, fetch it by ID. This is how a later stage of your pipeline rehydrates the project it needs from a stored ID.
SDK
Use Project.get(...):
project = aip.Project.get(project_id)
API
GET the project by ID:
curl -sS "$AIP_API_URL/projects/<project_id>" \
-H "Authorization: Bearer $AIP_API_KEY"
Listing projects (optional)
To see what already exists — for example, to confirm your target project is present in the workspace — list projects, optionally filtered by schema or workspace. This is a reference call, not part of the core create-and-run flow.
- UI
- Python SDK
- REST API
Open Projects in the console. Select the workspace in the top bar, then use Search projects… to find the project by name.
After authenticating the SDK, use
Project.list to filter by schema or workspace_id:
projects = aip.Project.list(schema_name="gdi_text_v1")
projects = aip.Project.list(workspace_id=ws.id)
Set AIP_API_URL and AIP_API_KEY for your deployment as described in
Authentication. GET /projects with an
optional schema_name query parameter:
curl -sS "$AIP_API_URL/projects?schema_name=gdi_text_v1" \
-H "Authorization: Bearer $AIP_API_KEY"
Moving a project to another workspace
A project born in a personal workspace can be promoted into a shared team
workspace without re-uploading its data — everything under it (datasets,
runs, eval configs) moves with it. A SUT scoped only to the project moves
too; a SUT shared at the workspace level is copied or reused in the target
instead, so other projects still using it in the source workspace keep
working. Requires workspace_admin/workspace_editor on both the source
and target workspace (a platform admin bypasses both checks).
SDK
Project.move_to_workspace(...) updates the project in place:
project.move_to_workspace(target_workspace_id)
print(project.workspace_id) # now the target workspace
API
POST the target workspace ID:
curl -sS -X POST "$AIP_API_URL/projects/<project_id>/move" \
-H "Authorization: Bearer $AIP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"target_workspace_id": "<workspace_id>"}'
With a project in hand, the next step is to bring in the data you want to evaluate against it.