Skip to main content

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) or single_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.

Open Projects in the console. Select the workspace in the top bar, then use Search projects… to find the project by name.

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.