@@ -41,6 +41,10 @@ public interface BraintrustApiClient {
4141 /** Get project and org info for the given project ID */
4242 Optional <OrganizationAndProjectInfo > getProjectAndOrgInfo (String projectId );
4343
44+ // TODO: cache project+org info?
45+ /** Get project and org info for the given config. Creating them if necessary */
46+ OrganizationAndProjectInfo getOrCreateProjectAndOrgInfo (BraintrustConfig config );
47+
4448 /** Get a prompt by slug and optional version */
4549 Optional <Prompt > getPrompt (
4650 @ Nonnull String projectName , @ Nonnull String slug , @ Nullable String version );
@@ -145,6 +149,43 @@ public Optional<OrganizationAndProjectInfo> getProjectAndOrgInfo(String projectI
145149 return Optional .of (new OrganizationAndProjectInfo (orgInfo , project ));
146150 }
147151
152+ @ Override
153+ public OrganizationAndProjectInfo getOrCreateProjectAndOrgInfo (BraintrustConfig config ) {
154+ // Get or create project based on config
155+ Project project ;
156+ if (config .defaultProjectId ().isPresent ()) {
157+ var projectId = config .defaultProjectId ().get ();
158+ project =
159+ getProject (projectId )
160+ .orElseThrow (
161+ () ->
162+ new ApiException (
163+ "Project with ID '"
164+ + projectId
165+ + "' not found" ));
166+ } else if (config .defaultProjectName ().isPresent ()) {
167+ var projectName = config .defaultProjectName ().get ();
168+ project = getOrCreateProject (projectName );
169+ } else {
170+ throw new ApiException (
171+ "Either project ID or project name must be provided in config" );
172+ }
173+
174+ // Fetch organization info
175+ OrganizationInfo orgInfo = null ;
176+ for (var org : login ().orgInfo ()) {
177+ if (project .orgId ().equalsIgnoreCase (org .id ())) {
178+ orgInfo = org ;
179+ break ;
180+ }
181+ }
182+ if (null == orgInfo ) {
183+ throw new ApiException ("Unable to find organization for project: " + project .id ());
184+ }
185+
186+ return new OrganizationAndProjectInfo (orgInfo , project );
187+ }
188+
148189 @ Override
149190 public Optional <Prompt > getPrompt (
150191 @ Nonnull String projectName , @ Nonnull String slug , @ Nullable String version ) {
@@ -293,13 +334,14 @@ class InMemoryImpl implements BraintrustApiClient {
293334 private final List <Prompt > prompts = new ArrayList <>();
294335
295336 public InMemoryImpl (OrganizationAndProjectInfo ... organizationAndProjectInfos ) {
296- this .organizationAndProjectInfos = List .of (organizationAndProjectInfos );
337+ this .organizationAndProjectInfos =
338+ new ArrayList <>(List .of (organizationAndProjectInfos ));
297339 }
298340
299341 public InMemoryImpl (
300342 List <OrganizationAndProjectInfo > organizationAndProjectInfos ,
301343 List <Prompt > prompts ) {
302- this .organizationAndProjectInfos = organizationAndProjectInfos ;
344+ this .organizationAndProjectInfos = new ArrayList <>( organizationAndProjectInfos ) ;
303345 this .prompts .addAll (prompts );
304346 }
305347
@@ -311,11 +353,24 @@ public Project getOrCreateProject(String projectName) {
311353 return orgAndProject .project ();
312354 }
313355 }
314- throw new RuntimeException (
315- "Project '"
316- + projectName
317- + "' not found in test data. Please add it to the InMemoryImpl"
318- + " constructor." );
356+
357+ // Create new project if not found
358+ var defaultOrgInfo =
359+ organizationAndProjectInfos .isEmpty ()
360+ ? new OrganizationInfo ("default-org-id" , "Default Organization" )
361+ : organizationAndProjectInfos .get (0 ).orgInfo ();
362+
363+ var newProject =
364+ new Project (
365+ "project-" + UUID .randomUUID ().toString (),
366+ projectName ,
367+ defaultOrgInfo .id (),
368+ java .time .Instant .now ().toString (),
369+ java .time .Instant .now ().toString ());
370+
371+ organizationAndProjectInfos .add (
372+ new OrganizationAndProjectInfo (defaultOrgInfo , newProject ));
373+ return newProject ;
319374 }
320375
321376 @ Override
@@ -357,6 +412,39 @@ public Optional<OrganizationAndProjectInfo> getProjectAndOrgInfo(String projectI
357412 .findFirst ();
358413 }
359414
415+ @ Override
416+ public OrganizationAndProjectInfo getOrCreateProjectAndOrgInfo (BraintrustConfig config ) {
417+ // Get or create project based on config
418+ Project project ;
419+ if (config .defaultProjectId ().isPresent ()) {
420+ var projectId = config .defaultProjectId ().get ();
421+ project =
422+ getProject (projectId )
423+ .orElseThrow (
424+ () ->
425+ new ApiException (
426+ "Project with ID '"
427+ + projectId
428+ + "' not found" ));
429+ } else if (config .defaultProjectName ().isPresent ()) {
430+ var projectName = config .defaultProjectName ().get ();
431+ project = getOrCreateProject (projectName );
432+ } else {
433+ throw new ApiException (
434+ "Either project ID or project name must be provided in config" );
435+ }
436+
437+ // Find the organization info for this project
438+ return organizationAndProjectInfos .stream ()
439+ .filter (info -> info .project ().id ().equals (project .id ()))
440+ .findFirst ()
441+ .orElseThrow (
442+ () ->
443+ new ApiException (
444+ "Unable to find organization for project: "
445+ + project .id ()));
446+ }
447+
360448 @ Override
361449 public Optional <Prompt > getPrompt (
362450 @ Nonnull String projectName , @ Nonnull String slug , @ Nullable String version ) {
0 commit comments