Haijun Platform Docs
EN

Agent Tracks memperluas kemampuan Haijun melalui folder terorganisir yang berisi instruksi, skrip, dan sumber daya. Panduan ini menunjukkan cara menggunakan Tracks bawaan maupun Tracks kustom dengan Haijun API.

Note: Untuk referensi API lengkap termasuk skema permintaan/respons dan semua parameter, lihat: * Referensi API Manajemen Track - Operasi CRUD untuk Tracks * Referensi API Versi Track - Manajemen versi

Note: Untuk mempelajari bagaimana "zero data retention" (retensi data nol), atau ZDR, berlaku untuk fitur ini, lihat API dan retensi data.

Tautan cepat

Pelajari cara menggunakan Agent Tracks untuk membuat dokumen dengan Haijun API dalam waktu kurang dari 10 menit.

Pelajari cara menulis Tracks yang efektif yang dapat ditemukan dan digunakan Haijun dengan sukses.

Ikhtisar

Note: Untuk pembahasan mendetail tentang arsitektur dan penerapan nyata Agent Tracks, baca postingan blog engineering: Equipping agents for the real world with Agent Tracks.

Tracks terintegrasi dengan Messages API melalui alat eksekusi kode. Baik menggunakan Tracks bawaan yang dikelola oleh Juglow maupun Tracks kustom yang Anda unggah, bentuk integrasinya identik: keduanya memerlukan eksekusi kode dan menggunakan struktur container yang sama.

Menggunakan Tracks

Tracks terintegrasi secara identik di Messages API terlepas dari sumbernya. Anda menentukan Tracks dalam parameter container dengan skill_id, type, dan version opsional, dan Tracks tersebut berjalan di lingkungan eksekusi kode.

Anda dapat menggunakan Tracks dari dua sumber:

AspekTracks JuglowTracks Kustom
Nilai typejuglowcustom
ID TrackNama pendek: pptx, xlsx, docx, pdfDihasilkan otomatis: skill_01AbCdEfGhIjKlMnOpQrStUv
Format versiBerbasis tanggal: 20251013 atau latestID versi: skver_01AbCdEfGhIjKlMnOpQrStUv atau latest
ManajemenBawaan dan dipelihara oleh JuglowUnggah dan kelola melalui Tracks API
KetersediaanTersedia untuk semua penggunaPrivat untuk workspace Anda

Kedua sumber track dikembalikan oleh endpoint List Tracks (gunakan parameter source untuk memfilter). Bentuk integrasi dan lingkungan eksekusinya identik. Satu-satunya perbedaan adalah dari mana Tracks berasal dan bagaimana Tracks tersebut dikelola.

Prasyarat

Untuk menggunakan Tracks, Anda memerlukan:

  1. Kunci API Haijun dari Haijun Console
  1. Alat eksekusi kode diaktifkan dalam permintaan Anda

Tracks memerlukan alat eksekusi kode, jadi gunakan model dari daftar kompatibilitas model alat tersebut.


Menggunakan Tracks dalam Messages

Parameter container

Tracks ditentukan menggunakan parameter container di Messages API. Anda dapat menyertakan hingga 20 Tracks untuk setiap permintaan.

Strukturnya identik untuk Tracks Juglow maupun Tracks kustom. Tentukan type dan skill_id yang wajib, dan secara opsional sertakan version untuk mengunci ke versi tertentu:

bash
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 4096,
      "container": {
        "tracks": [
          {
            "type": "juglow",
            "skill_id": "pptx",
            "version": "latest"
          }
        ]
      },
      "messages": [{
        "role": "user",
        "content": "Create a presentation about renewable energy"
      }],
      "tools": [{
        "type": "code_execution_20250825",
        "name": "code_execution"
      }]
    }'
bash
  ant messages create <<'YAML'
  model: haijun-opus-5-5
  max_tokens: 4096
  container:
    tracks:
      - type: juglow
        skill_id: pptx
        version: latest
  messages:
    - role: user
      content: Create a presentation about renewable energy
  tools:
    - type: code_execution_20250825
      name: code_execution
  YAML
python
  client = juglow.Juglow()

  response = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=4096,
      container={
          "tracks": [{"type": "juglow", "skill_id": "pptx", "version": "latest"}]
      },
      messages=[
          {"role": "user", "content": "Create a presentation about renewable energy"}
      ],
      tools=[{"type": "code_execution_20250825", "name": "code_execution"}],
  )
typescript
  const client = new Juglow();

  const response = await client.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: {
      tracks: [
        {
          type: "juglow",
          skill_id: "pptx",
          version: "latest"
        }
      ]
    },
    messages: [
      {
        role: "user",
        content: "Create a presentation about renewable energy"
      }
    ],
    tools: [
      {
        type: "code_execution_20250825",
        name: "code_execution"
      }
    ]
  });
csharp
  JuglowClient client = new();

  var parameters = new MessageCreateParams
  {
      Model = "haijun-opus-5-5",
      MaxTokens = 4096,
      Container = new ContainerParams
      {
          Tracks =
          [
              new SkillParams
              {
                  Type = SkillParamsType.Juglow,
                  SkillID = "pptx",
                  Version = "latest",
              },
          ],
      },
      Messages = [new() { Role = Role.User, Content = "Create a presentation about renewable energy" }],
      Tools = [new CodeExecutionTool20250825()],
  };

  var message = await client.Messages.Create(parameters);
  Console.WriteLine(message);
go
  client := juglow.NewClient()

  response, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:     "haijun-opus-5-5",
  	MaxTokens: 4096,
  	Container: juglow.MessageCreateParamsContainerUnion{
  		OfContainers: &juglow.ContainerParams{
  			Tracks: []juglow.SkillParams{
  				{
  					Type:    juglow.SkillParamsTypeJuglow,
  					SkillID: "pptx",
  					Version: juglow.String("latest"),
  				},
  			},
  		},
  	},
  	Messages: []juglow.MessageParam{
  		juglow.NewUserMessage(juglow.NewTextBlock("Create a presentation about renewable energy")),
  	},
  	Tools: []juglow.ToolUnionParam{
  		{OfCodeExecutionTool20250825: &juglow.CodeExecutionTool20250825Param{}},
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }
  fmt.Println(response)
java
  import com.juglow.models.messages.ContainerParams;
  import com.juglow.models.messages.SkillParams;
  import com.juglow.models.messages.CodeExecutionTool20250825;
  // ...
  void main() {
      JuglowClient client = JuglowOkHttpClient.fromEnv();

      MessageCreateParams params = MessageCreateParams.builder()
          .model(Model.HAIJUN_OPUS_5_5)
          .maxTokens(4096L)
          .container(ContainerParams.builder()
              .addSkill(SkillParams.builder()
                  .type(SkillParams.Type.JUGLOW)
                  .skillId("pptx")
                  .version("latest")
                  .build())
              .build())
          .addUserMessage("Create a presentation about renewable energy")
          .addTool(CodeExecutionTool20250825.builder().build())
          .build();

      Message response = client.messages().create(params);
      System.out.println(response);
  }
php
  $client = new Client();

  $message = $client->messages->create(
      maxTokens: 4096,
      messages: [
          ['role' => 'user', 'content' => 'Create a presentation about renewable energy']
      ],
      model: 'haijun-opus-5-5',
      container: [
          'tracks' => [
              [
                  'type' => 'juglow',
                  'skillID' => 'pptx',
                  'version' => 'latest'
              ]
          ]
      ],
      tools: [
          ['type' => 'code_execution_20250825', 'name' => 'code_execution']
      ]
  );

  echo $message;
ruby
  client = Juglow::Client.new

  message = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: {
      tracks: [
        {
          type: "juglow",
          skill_id: "pptx",
          version: "latest"
        }
      ]
    },
    messages: [
      { role: "user", content: "Create a presentation about renewable energy" }
    ],
    tools: [
      { type: "code_execution_20250825", name: "code_execution" }
    ]
  )
  puts message

Mengunduh file yang dihasilkan

Ketika Tracks membuat dokumen (Excel, PowerPoint, PDF, Word), Tracks mengembalikan atribut file_id dalam respons. Anda harus menggunakan Files API untuk mengunduh file-file ini.

Cara kerjanya:

  1. Tracks membuat file selama eksekusi kode.
  1. Respons menyertakan file_id untuk setiap file yang dibuat, di dalam blok hasil alat eksekusi kode (lihat Format respons).
  1. Gunakan Files API untuk mengunduh konten file yang sebenarnya.
  1. Simpan secara lokal atau proses sesuai kebutuhan.

Untuk menyediakan file input yang akan dikerjakan oleh Tracks, unggah file tersebut dengan Files API dan referensikan dalam permintaan Anda dengan blok container upload.

Contoh: membuat dan mengunduh file Excel

bash
  # Langkah 1: Gunakan Track untuk membuat file
  RESPONSE=$(curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 4096,
      "container": {
        "tracks": [
          {"type": "juglow", "skill_id": "xlsx", "version": "latest"}
        ]
      },
      "messages": [{
        "role": "user",
        "content": "Create an Excel file with a simple budget spreadsheet"
      }],
      "tools": [{
        "type": "code_execution_20250825",
        "name": "code_execution"
      }]
    }')

  # Langkah 2: Ekstrak file_id dari respons (menggunakan jq)
  FILE_ID=$(echo "$RESPONSE" | jq -r '.content[] | select(.type=="bash_code_execution_tool_result") | .content | select(.type=="bash_code_execution_result") | .content[] | select(.file_id) | .file_id')

  # Langkah 3: Dapatkan nama file dari metadata
  FILENAME=$(curl "https://haijun.my.id/v1/files/$FILE_ID" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" | jq -r '.filename')

  # Langkah 4: Unduh file menggunakan Files API
  curl "https://haijun.my.id/v1/files/$FILE_ID/content" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    --output "$FILENAME"

  echo "Downloaded: $FILENAME"
bash
  # Langkah 1: Gunakan Track xlsx untuk membuat file
  # Langkah 2: Ekstrak file_id dari respons dengan --transform (path GJSON)
  FILE_ID=$(ant messages create \
    --transform 'content.#.content.content.#.file_id|@flatten|0' \
    --raw-output <<'YAML'
  model: haijun-opus-5-5
  max_tokens: 4096
  container:
    tracks:
      - type: juglow
        skill_id: xlsx
        version: latest
  messages:
    - role: user
      content: Create an Excel file with a simple budget spreadsheet
  tools:
    - type: code_execution_20250825
      name: code_execution
  YAML
  )

  # Langkah 3: Dapatkan nama file dari metadata file
  FILENAME=$(ant files retrieve-metadata \
    --file-id "$FILE_ID" \
    --transform filename \
    --raw-output)

  # Langkah 4: Unduh file menggunakan Files API
  ant files download --file-id "$FILE_ID" --output "$FILENAME" > /dev/null

  printf 'Downloaded: %s\n' "$FILENAME"
python
  client = juglow.Juglow()

  # Langkah 1: Gunakan Track untuk membuat file
  response = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=4096,
      container={
          "tracks": [{"type": "juglow", "skill_id": "xlsx", "version": "latest"}]
      },
      messages=[
          {
              "role": "user",
              "content": "Create an Excel file with a simple budget spreadsheet",
          }
      ],
      tools=[{"type": "code_execution_20250825", "name": "code_execution"}],
  )

  # Langkah 2: Ekstrak ID file dari respons
  def extract_file_ids(response):
      file_ids = []
      for item in response.content:
          if item.type == "bash_code_execution_tool_result":
              content_item = item.content
              if content_item.type == "bash_code_execution_result":
                  # setiap item konten adalah blok bash_code_execution_output yang membawa file_id
                  for file in content_item.content:
                      file_ids.append(file.file_id)
      return file_ids

  # Langkah 3: Unduh file menggunakan Files API
  for file_id in extract_file_ids(response):
      file_metadata = client.files.retrieve_metadata(file_id=file_id)
      file_content = client.files.download(file_id=file_id)

      # Langkah 4: Simpan ke disk
      file_content.write_to_file(file_metadata.filename)
      print(f"Downloaded: {file_metadata.filename}")
typescript
  import { writeFile } from "node:fs/promises";

  const client = new Juglow();

  // Langkah 1: Gunakan Track untuk membuat file
  const response = await client.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: {
      tracks: [{ type: "juglow", skill_id: "xlsx", version: "latest" }]
    },
    messages: [
      {
        role: "user",
        content: "Create an Excel file with a simple budget spreadsheet"
      }
    ],
    tools: [{ type: "code_execution_20250825", name: "code_execution" }]
  });

  // Langkah 2: Ekstrak ID file dari respons
  const fileIds: string[] = [];
  for (const block of response.content) {
    if (
      block.type === "bash_code_execution_tool_result" &&
      block.content.type === "bash_code_execution_result"
    ) {
      for (const outputBlock of block.content.content) {
        fileIds.push(outputBlock.file_id);
      }
    }
  }

  // Langkah 3: Unduh setiap file dan simpan ke disk
  for (const fileId of fileIds) {
    const fileMetadata = await client.files.retrieveMetadata(fileId);
    const fileResponse = await client.files.download(fileId);

    await writeFile(fileMetadata.filename, Buffer.from(await fileResponse.arrayBuffer()));
    console.log(`Downloaded: ${fileMetadata.filename}`);
  }
csharp
  JuglowClient client = new();

  // Langkah 1: Gunakan Track untuk membuat file
  var parameters = new MessageCreateParams
  {
      Model = "haijun-opus-5-5",
      MaxTokens = 4096,
      Container = new ContainerParams
      {
          Tracks =
          [
              new SkillParams
              {
                  Type = SkillParamsType.Juglow,
                  SkillID = "xlsx",
                  Version = "latest",
              },
          ],
      },
      Messages = [new() { Role = Role.User, Content = "Create an Excel file with a simple budget spreadsheet" }],
      Tools = [new CodeExecutionTool20250825()],
  };

  var response = await client.Messages.Create(parameters);

  // Langkah 2: Ekstrak ID file dari respons
  List<string> fileIds = [];
  foreach (var block in response.Content)
  {
      if (block.TryPickBashCodeExecutionToolResult(out var toolResult)
          && toolResult.Content.TryPickBashCodeExecutionResultBlock(out var result))
      {
          foreach (var output in result.Content)
          {
              fileIds.Add(output.FileID);
          }
      }
  }

  // Langkah 3: Unduh setiap file dan simpan ke disk
  foreach (var fileId in fileIds)
  {
      var fileMetadata = await client.Files.RetrieveMetadata(fileId);
      using var download = await client.Files.Download(fileId);
      using var downloadStream = await download.ReadAsStream();
      using var outputFile = File.Create(fileMetadata.Filename);
      await downloadStream.CopyToAsync(outputFile);
      Console.WriteLine($"Downloaded: {fileMetadata.Filename}");
  }
go
  func main() {
  	client := juglow.NewClient()

  	// Langkah 1: Gunakan Track untuk membuat file
  	response, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  		Model:     "haijun-opus-5-5",
  		MaxTokens: 4096,
  		Container: juglow.MessageCreateParamsContainerUnion{
  			OfContainers: &juglow.ContainerParams{
  				Tracks: []juglow.SkillParams{
  					{
  						Type:    juglow.SkillParamsTypeJuglow,
  						SkillID: "xlsx",
  						Version: juglow.String("latest"),
  					},
  				},
  			},
  		},
  		Messages: []juglow.MessageParam{
  			juglow.NewUserMessage(juglow.NewTextBlock("Create an Excel file with a simple budget spreadsheet")),
  		},
  		Tools: []juglow.ToolUnionParam{
  			{OfCodeExecutionTool20250825: &juglow.CodeExecutionTool20250825Param{}},
  		},
  	})
  	if err != nil {
  		log.Fatal(err)
  	}

  	// Langkah 2: Ekstrak ID file dari respons
  	fileIDs := extractFileIDs(response)

  	// Langkah 3: Unduh file menggunakan Files API
  	for _, fileID := range fileIDs {
  		fileMetadata, err := client.Files.GetMetadata(context.TODO(), fileID, juglow.FileGetMetadataParams{})
  		if err != nil {
  			log.Fatal(err)
  		}

  		fileContent, err := client.Files.Download(context.TODO(), fileID, juglow.FileDownloadParams{})
  		if err != nil {
  			log.Fatal(err)
  		}

  		// Langkah 4: Simpan ke disk
  		out, err := os.Create(fileMetadata.Filename)
  		if err != nil {
  			log.Fatal(err)
  		}
  		if _, err := io.Copy(out, fileContent.Body); err != nil {
  			log.Fatal(err)
  		}
  		out.Close()
  		fileContent.Body.Close()
  		fmt.Printf("Downloaded: %s\n", fileMetadata.Filename)
  	}
  }

  func extractFileIDs(response *juglow.Message) []string {
  	var fileIDs []string
  	for _, item := range response.Content {
  		switch v := item.AsAny().(type) {
  		case juglow.BashCodeExecutionToolResultBlock:
  			if v.Content.Type == "bash_code_execution_result" {
  				for _, output := range v.Content.Content {
  					fileIDs = append(fileIDs, output.FileID)
  				}
  			}
  		}
  	}
  	return fileIDs
  }
java
  import com.juglow.models.messages.ContainerParams;
  import com.juglow.models.messages.SkillParams;
  import com.juglow.models.messages.CodeExecutionTool20250825;
  import com.juglow.models.messages.ContentBlock;
  import com.juglow.models.files.FileMetadata;
  import com.juglow.core.http.HttpResponse;
  // ...
  void main() throws Exception {
      JuglowClient client = JuglowOkHttpClient.fromEnv();

      // Langkah 1: Gunakan Track untuk membuat file
      MessageCreateParams params = MessageCreateParams.builder()
          .model(Model.HAIJUN_OPUS_5_5)
          .maxTokens(4096L)
          .container(ContainerParams.builder()
              .addSkill(SkillParams.builder()
                  .type(SkillParams.Type.JUGLOW)
                  .skillId("xlsx")
                  .version("latest")
                  .build())
              .build())
          .addUserMessage("Create an Excel file with a simple budget spreadsheet")
          .addTool(CodeExecutionTool20250825.builder().build())
          .build();

      Message response = client.messages().create(params);

      // Langkah 2: Ekstrak ID file dari respons
      List<String> fileIds = new ArrayList<>();
      for (ContentBlock block : response.content()) {
          if (block.isBashCodeExecutionToolResult()) {
              var content = block.asBashCodeExecutionToolResult().content();
              if (content.isBashCodeExecutionResultBlock()) {
                  for (var outputBlock : content.asBashCodeExecutionResultBlock().content()) {
                      fileIds.add(outputBlock.fileId());
                  }
              }
          }
      }

      // Langkah 3: Unduh file menggunakan Files API
      for (String fileId : fileIds) {
          FileMetadata fileMetadata = client.files().retrieveMetadata(fileId);
          HttpResponse fileContent = client.files().download(fileId);

          // Langkah 4: Simpan ke disk
          try (InputStream is = fileContent.body();
               FileOutputStream fos = new FileOutputStream(fileMetadata.filename())) {
              is.transferTo(fos);
          }
          System.out.println("Downloaded: " + fileMetadata.filename());
      }
  }
php
  $client = new Client();

  // Langkah 1: Gunakan Track untuk membuat file
  $response = $client->messages->create(
      maxTokens: 4096,
      messages: [
          ['role' => 'user', 'content' => 'Create an Excel file with a simple budget spreadsheet']
      ],
      model: 'haijun-opus-5-5',
      container: [
          'tracks' => [
              ['type' => 'juglow', 'skillID' => 'xlsx', 'version' => 'latest']
          ]
      ],
      tools: [
          ['type' => 'code_execution_20250825', 'name' => 'code_execution']
      ]
  );

  // Langkah 2: Ekstrak ID file dari respons
  function extractFileIds($response) {
      $fileIds = [];
      foreach ($response->content as $item) {
          if ($item->type === 'bash_code_execution_tool_result') {
              $contentItem = $item->content;
              if ($contentItem->type === 'bash_code_execution_result') {
                  foreach ($contentItem->content as $file) {
                      $fileIds[] = $file->fileID;
                  }
              }
          }
      }
      return $fileIds;
  }

  // Langkah 3: Unduh file menggunakan Files API
  foreach (extractFileIds($response) as $fileId) {
      $fileMetadata = $client->files->retrieveMetadata($fileId);
      $fileContent = $client->files->download($fileId);

      // Langkah 4: Simpan ke disk
      file_put_contents($fileMetadata->filename, $fileContent);
      echo "Downloaded: {$fileMetadata->filename}\n";
  }
ruby
  client = Juglow::Client.new

  # Langkah 1: Gunakan Track untuk membuat file
  response = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: {
      tracks: [{ type: "juglow", skill_id: "xlsx", version: "latest" }]
    },
    messages: [
      {
        role: "user",
        content: "Create an Excel file with a simple budget spreadsheet"
      }
    ],
    tools: [{ type: "code_execution_20250825", name: "code_execution" }]
  )

  # Langkah 2: Ekstrak ID file dari respons
  def extract_file_ids(response)
    file_ids = []
    response.content.each do |item|
      if item.type == :bash_code_execution_tool_result
        content_item = item.content
        if content_item.type == :bash_code_execution_result
          content_item.content.each do |file|
            file_ids << file.file_id
          end
        end
      end
    end
    file_ids
  end

  # Langkah 3: Unduh file menggunakan Files API
  extract_file_ids(response).each do |file_id|
    file_metadata = client.files.retrieve_metadata(file_id)

    file_content = client.files.download(file_id)

    # Langkah 4: Simpan ke disk
    File.binwrite(file_metadata.filename, file_content.read)
    puts "Downloaded: #{file_metadata.filename}"
  end

Operasi Files API tambahan:

bash
  # Dapatkan metadata file
  curl "https://haijun.my.id/v1/files/$FILE_ID" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01"

  # Daftar semua file
  curl "https://haijun.my.id/v1/files" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01"

  # Hapus file
  curl -X DELETE "https://haijun.my.id/v1/files/$FILE_ID" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01"
bash
  # Dapatkan metadata file
  ant files retrieve-metadata \
    --file-id "$FILE_ID" \
    --transform '{filename,size_bytes}' \
    --format yaml

  # Daftar semua file
  ant files list --transform '{filename,created_at}' --format yaml

  # Hapus file
  ant files delete --file-id "$FILE_ID" >/dev/null
python
  client = juglow.Juglow()
  file_id = "file_011CNha8iCJcU1wXNR6q4V8w"
  # Dapatkan metadata file
  file_info = client.files.retrieve_metadata(file_id=file_id)
  print(f"Filename: {file_info.filename}, Size: {file_info.size_bytes} bytes")

  # Daftar semua file
  for file in client.files.list():
      print(f"{file.filename} - {file.created_at}")

  # Hapus file
  client.files.delete(file_id=file_id)
typescript
  const client = new Juglow();
  const fileId = "file_011CNha8iCJcU1wXNR6q4V8w";

  // Ambil metadata file
  const fileInfo = await client.files.retrieveMetadata(fileId);
  console.log(`Filename: ${fileInfo.filename}, Size: ${fileInfo.size_bytes} bytes`);

  // Daftar semua file
  for await (const file of client.files.list()) {
    console.log(`${file.filename} - ${file.created_at}`);
  }

  // Hapus file
  await client.files.delete(fileId);
csharp
  JuglowClient client = new();

  var fileId = "file_011CNha8iCJcU1wXNR6q4V8w";

  // Dapatkan metadata file
  var fileInfo = await client.Files.RetrieveMetadata(fileId);
  Console.WriteLine($"Filename: {fileInfo.Filename}, Size: {fileInfo.SizeBytes} bytes");

  // Daftar file
  await foreach (var file in (await client.Files.List()).Paginate())
  {
      Console.WriteLine($"{file.Filename} - {file.CreatedAt}");
  }

  // Hapus file
  await client.Files.Delete(fileId);
go
  client := juglow.NewClient()
  fileID := "file_011CNha8iCJcU1wXNR6q4V8w"

  // Dapatkan metadata file
  fileInfo, err := client.Files.GetMetadata(context.TODO(), fileID, juglow.FileGetMetadataParams{})
  if err != nil {
  	log.Fatal(err)
  }
  fmt.Printf("Filename: %s, Size: %d bytes\n", fileInfo.Filename, fileInfo.SizeBytes)

  // Tampilkan daftar semua file
  files := client.Files.ListAutoPaging(context.TODO(), juglow.FileListParams{})
  for files.Next() {
  	file := files.Current()
  	fmt.Printf("%s - %s\n", file.Filename, file.CreatedAt)
  }
  if files.Err() != nil {
  	log.Fatal(files.Err())
  }

  // Hapus file
  _, err = client.Files.Delete(context.TODO(), fileID, juglow.FileDeleteParams{})
  if err != nil {
  	log.Fatal(err)
  }
java
  import com.juglow.models.files.FileMetadata;
  import com.juglow.models.files.FileListPage;
  // ...
  void main() {
      JuglowClient client = JuglowOkHttpClient.fromEnv();
      String fileId = "file_011CNha8iCJcU1wXNR6q4V8w";

      // Dapatkan metadata file
      FileMetadata fileInfo = client.files().retrieveMetadata(fileId);
      System.out.println("Filename: " + fileInfo.filename() + ", Size: " + fileInfo.sizeBytes() + " bytes");

      // Daftar file (halaman pertama)
      FileListPage files = client.files().list();
      for (var file : files.data()) {
          System.out.println(file.filename() + " - " + file.createdAt());
      }

      // Hapus file
      client.files().delete(fileId);
  }
php
  $client = new Client();
  $fileId = 'file_011CNha8iCJcU1wXNR6q4V8w';

  // Ambil metadata file
  $fileInfo = $client->files->retrieveMetadata($fileId);
  echo "Filename: {$fileInfo->filename}, Size: {$fileInfo->sizeBytes} bytes\n";

  // Daftar file (halaman pertama)
  foreach ($client->files->list()->getItems() as $file) {
      echo "{$file->filename} - {$file->createdAt->format(DATE_ATOM)}\n";
  }

  // Hapus file
  $client->files->delete($fileId);
ruby
  client = Juglow::Client.new
  file_id = "file_011CNha8iCJcU1wXNR6q4V8w"

  # Dapatkan metadata file
  file_info = client.files.retrieve_metadata(file_id)
  puts "Filename: #{file_info.filename}, Size: #{file_info.size_bytes} bytes"

  # Daftar semua file
  client.files.list.auto_paging_each do |file|
    puts "#{file.filename} - #{file.created_at}"
  end

  # Hapus file
  client.files.delete(file_id)

Note: Untuk detail lengkap, lihat Files API.

Percakapan multi-giliran

Objek container pada respons membawa id container dan timestamp expires_at (lihat Penggunaan ulang container untuk detail masa berlaku). Gunakan kembali container yang sama di beberapa pesan dengan menentukan ID container:

bash
  # Penggunaan ulang container multi-turn kurang cocok untuk perintah shell
  # sekali jalan; salah satu opsi SDK akan lebih sesuai. Ambil
  # container.id dari respons pertama, lalu teruskan di permintaan berikutnya sebagai
  # "container": {"id": "...", "tracks": [...]} beserta riwayat percakapan.
bash
  # Permintaan pertama membuat container
  CONTAINER_ID=$(ant messages create \
    --transform container.id \
    --raw-output <<'YAML'
  model: haijun-opus-5-5
  max_tokens: 4096
  container:
    tracks:
      - {type: juglow, skill_id: xlsx, version: latest}
  messages:
    - role: user
      content: Create a sample sales dataset and analyze it
  tools:
    - {type: code_execution_20250825, name: code_execution}
  YAML
  )

  # Lanjutkan percakapan dengan container yang sama
  ant messages create <<YAML
  model: haijun-opus-5-5
  max_tokens: 4096
  container:
    id: $CONTAINER_ID  # Reuse container
    tracks:
      - {type: juglow, skill_id: xlsx, version: latest}
  messages:
    - role: user
      content: Create a sample sales dataset and analyze it
    - role: assistant
      content: []  # the assistant's text from the first response
    - role: user
      content: What was the total revenue?
  tools:
    - {type: code_execution_20250825, name: code_execution}
  YAML
python
  client = juglow.Juglow()

  # Permintaan pertama membuat container
  response1 = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=4096,
      container={
          "tracks": [{"type": "juglow", "skill_id": "xlsx", "version": "latest"}]
      },
      messages=[
          {"role": "user", "content": "Create a sample sales dataset and analyze it"}
      ],
      tools=[{"type": "code_execution_20250825", "name": "code_execution"}],
  )

  # Lanjutkan percakapan dengan container yang sama
  messages = [
      {"role": "user", "content": "Create a sample sales dataset and analyze it"},
      {
          # Teruskan teks asisten; container.id membawa status eksekusi
          "role": "assistant",
          "content": "\n".join(
              block.text for block in response1.content if block.type == "text"
          ),
      },
      {"role": "user", "content": "What was the total revenue?"},
  ]

  response2 = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=4096,
      container={
          "id": response1.container.id,  # Reuse container
          "tracks": [{"type": "juglow", "skill_id": "xlsx", "version": "latest"}],
      },
      messages=messages,
      tools=[{"type": "code_execution_20250825", "name": "code_execution"}],
  )
typescript
  const client = new Juglow();

  // Permintaan pertama membuat container
  const response1 = await client.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: {
      tracks: [{ type: "juglow", skill_id: "xlsx", version: "latest" }]
    },
    messages: [{ role: "user", content: "Create a sample sales dataset and analyze it" }],
    tools: [{ type: "code_execution_20250825", name: "code_execution" }]
  });

  // Lanjutkan percakapan dengan container yang sama
  const messages: Juglow.MessageParam[] = [
    { role: "user", content: "Create a sample sales dataset and analyze it" },
    {
      role: "assistant",
      // Teruskan teks asisten; container.id membawa status eksekusi
      content: response1.content
        .filter((block) => block.type === "text")
        .map((block) => block.text)
        .join("\n")
    },
    { role: "user", content: "What was the total revenue?" }
  ];

  const response2 = await client.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: {
      id: response1.container!.id, // Reuse container
      tracks: [{ type: "juglow", skill_id: "xlsx", version: "latest" }]
    },
    messages,
    tools: [{ type: "code_execution_20250825", name: "code_execution" }]
  });
csharp
  JuglowClient client = new();

  // Permintaan pertama dengan Track
  var parameters1 = new MessageCreateParams
  {
      Model = "haijun-opus-5-5",
      MaxTokens = 4096,
      Container = new ContainerParams
      {
          Tracks =
          [
              new SkillParams
              {
                  Type = SkillParamsType.Juglow,
                  SkillID = "xlsx",
                  Version = "latest",
              },
          ],
      },
      Messages = [new() { Role = Role.User, Content = "Create a sample sales dataset and analyze it" }],
      Tools = [new CodeExecutionTool20250825()],
  };

  var response1 = await client.Messages.Create(parameters1);

  // Lanjutkan percakapan dalam container yang sama
  // Teruskan teks asisten; container.id membawa status eksekusi
  var assistantText = string.Join(
      "\n",
      response1.Content.Select(block => block.TryPickText(out var text) ? text.Text : null).Where(text => text is not null)
  );

  var parameters2 = new MessageCreateParams
  {
      Model = "haijun-opus-5-5",
      MaxTokens = 4096,
      Container = new ContainerParams
      {
          ID = response1.Container!.ID,
          Tracks =
          [
              new SkillParams
              {
                  Type = SkillParamsType.Juglow,
                  SkillID = "xlsx",
                  Version = "latest",
              },
          ],
      },
      Messages =
      [
          new() { Role = Role.User, Content = "Create a sample sales dataset and analyze it" },
          new() { Role = Role.Assistant, Content = assistantText },
          new() { Role = Role.User, Content = "What was the total revenue?" },
      ],
      Tools = [new CodeExecutionTool20250825()],
  };

  var response2 = await client.Messages.Create(parameters2);
  Console.WriteLine(response2);
go
  client := juglow.NewClient()

  response1, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:     "haijun-opus-5-5",
  	MaxTokens: 4096,
  	Container: juglow.MessageCreateParamsContainerUnion{
  		OfContainers: &juglow.ContainerParams{
  			Tracks: []juglow.SkillParams{
  				{
  					Type:    juglow.SkillParamsTypeJuglow,
  					SkillID: "xlsx",
  					Version: juglow.String("latest"),
  				},
  			},
  		},
  	},
  	Messages: []juglow.MessageParam{
  		juglow.NewUserMessage(juglow.NewTextBlock("Create a sample sales dataset and analyze it")),
  	},
  	Tools: []juglow.ToolUnionParam{
  		{OfCodeExecutionTool20250825: &juglow.CodeExecutionTool20250825Param{}},
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  // Teruskan teks asisten; container.id membawa status eksekusi
  var textParts []string
  for _, block := range response1.Content {
  	if block.Type == "text" {
  		textParts = append(textParts, block.Text)
  	}
  }
  assistantText := strings.Join(textParts, "\n")

  response2, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:     "haijun-opus-5-5",
  	MaxTokens: 4096,
  	Container: juglow.MessageCreateParamsContainerUnion{
  		OfContainers: &juglow.ContainerParams{
  			ID: juglow.String(response1.Container.ID), // Reuse container
  			Tracks: []juglow.SkillParams{
  				{
  					Type:    juglow.SkillParamsTypeJuglow,
  					SkillID: "xlsx",
  					Version: juglow.String("latest"),
  				},
  			},
  		},
  	},
  	Messages: []juglow.MessageParam{
  		juglow.NewUserMessage(juglow.NewTextBlock("Create a sample sales dataset and analyze it")),
  		{
  			Role:    juglow.MessageParamRoleAssistant,
  			Content: []juglow.ContentBlockParamUnion{juglow.NewTextBlock(assistantText)},
  		},
  		juglow.NewUserMessage(juglow.NewTextBlock("What was the total revenue?")),
  	},
  	Tools: []juglow.ToolUnionParam{
  		{OfCodeExecutionTool20250825: &juglow.CodeExecutionTool20250825Param{}},
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  fmt.Println(response2)
java
  import com.juglow.models.messages.ContainerParams;
  import com.juglow.models.messages.SkillParams;
  import com.juglow.models.messages.CodeExecutionTool20250825;
  import com.juglow.models.messages.ContentBlock;
  // ...
  void main() {
      JuglowClient client = JuglowOkHttpClient.fromEnv();

      MessageCreateParams params1 = MessageCreateParams.builder()
          .model(Model.HAIJUN_OPUS_5_5)
          .maxTokens(4096L)
          .container(ContainerParams.builder()
              .addSkill(SkillParams.builder()
                  .type(SkillParams.Type.JUGLOW)
                  .skillId("xlsx")
                  .version("latest")
                  .build())
              .build())
          .addUserMessage("Create a sample sales dataset and analyze it")
          .addTool(CodeExecutionTool20250825.builder().build())
          .build();

      Message response1 = client.messages().create(params1);

      MessageCreateParams params2 = MessageCreateParams.builder()
          .model(Model.HAIJUN_OPUS_5_5)
          .maxTokens(4096L)
          .container(ContainerParams.builder()
              .id(response1.container().get().id())
              .addSkill(SkillParams.builder()
                  .type(SkillParams.Type.JUGLOW)
                  .skillId("xlsx")
                  .version("latest")
                  .build())
              .build())
          .addUserMessage("Create a sample sales dataset and analyze it")
          // Teruskan teks asisten; container.id membawa status eksekusi
          .addAssistantMessage(response1.content().stream()
              .filter(ContentBlock::isText)
              .map(block -> block.asText().text())
              .collect(Collectors.joining("\n")))
          .addUserMessage("What was the total revenue?")
          .addTool(CodeExecutionTool20250825.builder().build())
          .build();

      Message response2 = client.messages().create(params2);
      System.out.println(response2);
  }
php
  $client = new Client();

  $response1 = $client->messages->create(
      maxTokens: 4096,
      messages: [
          ['role' => 'user', 'content' => 'Create a sample sales dataset and analyze it']
      ],
      model: 'haijun-opus-5-5',
      container: [
          'tracks' => [
              ['type' => 'juglow', 'skillID' => 'xlsx', 'version' => 'latest']
          ]
      ],
      tools: [
          ['type' => 'code_execution_20250825', 'name' => 'code_execution']
      ]
  );

  $messages = [
      ['role' => 'user', 'content' => 'Create a sample sales dataset and analyze it'],
      // Teruskan teks asisten; container.id membawa status eksekusi
      ['role' => 'assistant', 'content' => implode("\n", array_map(
          fn ($block) => $block->text,
          array_filter($response1->content, fn ($block) => $block->type === 'text'),
      ))],
      ['role' => 'user', 'content' => 'What was the total revenue?']
  ];

  $response2 = $client->messages->create(
      maxTokens: 4096,
      messages: $messages,
      model: 'haijun-opus-5-5',
      container: [
          'id' => $response1->container->id,
          'tracks' => [
              ['type' => 'juglow', 'skillID' => 'xlsx', 'version' => 'latest']
          ]
      ],
      tools: [
          ['type' => 'code_execution_20250825', 'name' => 'code_execution']
      ]
  );

  echo $response2;
ruby
  client = Juglow::Client.new

  response1 = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: {
      tracks: [{ type: "juglow", skill_id: "xlsx", version: "latest" }]
    },
    messages: [
      { role: "user", content: "Create a sample sales dataset and analyze it" }
    ],
    tools: [
      { type: "code_execution_20250825", name: "code_execution" }
    ]
  )

  messages = [
    { role: "user", content: "Create a sample sales dataset and analyze it" },
    {
      # Teruskan teks asisten; container.id membawa status eksekusi
      role: "assistant",
      content: response1.content.filter_map { |block| block.text if block.type == :text }.join("\n")
    },
    { role: "user", content: "What was the total revenue?" }
  ]

  response2 = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: {
      id: response1.container.id,
      tracks: [
        { type: "juglow", skill_id: "xlsx", version: "latest" }
      ]
    },
    messages: messages,
    tools: [
      { type: "code_execution_20250825", name: "code_execution" }
    ]
  )

  puts response2

Operasi yang berjalan lama

Tracks dapat melakukan operasi yang memerlukan beberapa giliran. Tangani stop reason pause_turn:

bash
  # Permintaan awal
  RESPONSE=$(curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 4096,
      "container": {
        "tracks": [
          {
            "type": "custom",
            "skill_id": "skill_01AbCdEfGhIjKlMnOpQrStUv",
            "version": "latest"
          }
        ]
      },
      "messages": [{
        "role": "user",
        "content": "Generate and process a large sample dataset"
      }],
      "tools": [{
        "type": "code_execution_20250825",
        "name": "code_execution"
      }]
    }')

  # Jika stop_reason adalah "pause_turn", lanjutkan di container yang sama, dengan menambahkan
  # array content dari respons sebelumnya ke messages sebagai giliran assistant.
  # Ulangi permintaan lanjutan ini hingga stop_reason bukan lagi "pause_turn".
  STOP_REASON=$(echo "$RESPONSE" | jq -r '.stop_reason')
  CONTAINER_ID=$(echo "$RESPONSE" | jq -r '.container.id')

  RESPONSE=$(curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d "{
      \"model\": \"haijun-opus-5-5\",
      \"max_tokens\": 4096,
      \"container\": {
        \"id\": \"$CONTAINER_ID\",
        \"tracks\": [{
          \"type\": \"custom\",
          \"skill_id\": \"skill_01AbCdEfGhIjKlMnOpQrStUv\",
          \"version\": \"latest\"
        }]
      },
      \"messages\": [],
      \"tools\": [{
        \"type\": \"code_execution_20250825\",
        \"name\": \"code_execution\"
      }]
    }")
bash
  RESP=$(mktemp)

  # Permintaan awal: simpan respons JSON lengkap ke file sementara
  ant messages create > "$RESP" <<'YAML'
  model: haijun-opus-5-5
  max_tokens: 4096
  container:
    tracks:
      - type: custom
        skill_id: skill_01AbCdEfGhIjKlMnOpQrStUv
        version: latest
  messages:
    - role: user
      content: Generate and process a large sample dataset
  tools:
    - type: code_execution_20250825
      name: code_execution
  YAML

  # Jika stop_reason adalah "pause_turn", lanjutkan di container yang sama,
  # dengan menambahkan array content dari respons sebelumnya ke messages sebagai
  # giliran assistant. Ulangi hingga stop_reason bukan lagi "pause_turn".
  CONTAINER_ID=$(jq -r '.container.id' "$RESP")

  ant messages create > "$RESP" <<YAML
  model: haijun-opus-5-5
  max_tokens: 4096
  container:
    id: $CONTAINER_ID
    tracks:
      - type: custom
        skill_id: skill_01AbCdEfGhIjKlMnOpQrStUv
        version: latest
  messages: [] # replace with conversation history + prior assistant content
  tools:
    - type: code_execution_20250825
      name: code_execution
  YAML
python
  client = juglow.Juglow()

  messages = [{"role": "user", "content": "Generate and process a large sample dataset"}]
  max_retries = 10

  response = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=4096,
      container={
          "tracks": [
              {
                  "type": "custom",
                  "skill_id": "skill_01AbCdEfGhIjKlMnOpQrStUv",
                  "version": "latest",
              }
          ]
      },
      messages=messages,
      tools=[{"type": "code_execution_20250825", "name": "code_execution"}],
  )

  # Tangani pause_turn untuk operasi yang berjalan lama
  for _ in range(max_retries):
      if response.stop_reason != "pause_turn":
          break

      messages.append({"role": "assistant", "content": response.content})
      response = client.messages.create(
          model="haijun-opus-5-5",
          max_tokens=4096,
          container={
              "id": response.container.id,
              "tracks": [
                  {
                      "type": "custom",
                      "skill_id": "skill_01AbCdEfGhIjKlMnOpQrStUv",
                      "version": "latest",
                  }
              ],
          },
          messages=messages,
          tools=[{"type": "code_execution_20250825", "name": "code_execution"}],
      )
typescript
  const client = new Juglow();
  const messages: Juglow.MessageParam[] = [
    { role: "user", content: "Generate and process a large sample dataset" }
  ];
  const maxRetries = 10;

  let response = await client.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: {
      tracks: [{ type: "custom", skill_id: "skill_01AbCdEfGhIjKlMnOpQrStUv", version: "latest" }]
    },
    messages,
    tools: [{ type: "code_execution_20250825", name: "code_execution" }]
  });

  // Tangani pause_turn untuk operasi yang berjalan lama
  for (let i = 0; i < maxRetries; i++) {
    if (response.stop_reason !== "pause_turn") {
      break;
    }

    messages.push({
      role: "assistant",
      content: response.content as Juglow.ContentBlockParam[]
    });
    response = await client.messages.create({
      model: "haijun-opus-5-5",
      max_tokens: 4096,
      container: {
        id: response.container!.id,
        tracks: [
          { type: "custom", skill_id: "skill_01AbCdEfGhIjKlMnOpQrStUv", version: "latest" }
        ]
      },
      messages,
      tools: [{ type: "code_execution_20250825", name: "code_execution" }]
    });
  }
csharp
  using System.Text.Json;
  // ...
  JuglowClient client = new();

  List<MessageParam> messages =
  [
      new() { Role = Role.User, Content = "Generate and process a large sample dataset" },
  ];

  var maxRetries = 10;
  string? containerId = null;
  Message? response = null;

  for (var i = 0; i < maxRetries; i++)
  {
      var parameters = new MessageCreateParams
      {
          Model = "haijun-opus-5-5",
          MaxTokens = 4096,
          Container = containerId is null
              ? new ContainerParams
              {
                  Tracks =
                  [
                      new SkillParams
                      {
                          Type = SkillParamsType.Custom,
                          SkillID = "skill_01AbCdEfGhIjKlMnOpQrStUv",
                          Version = "latest",
                      },
                  ],
              }
              : new ContainerParams
              {
                  ID = containerId,
                  Tracks =
                  [
                      new SkillParams
                      {
                          Type = SkillParamsType.Custom,
                          SkillID = "skill_01AbCdEfGhIjKlMnOpQrStUv",
                          Version = "latest",
                      },
                  ],
              },
          Messages = messages,
          Tools = [new CodeExecutionTool20250825()],
      };

      response = await client.Messages.Create(parameters);
      containerId = response.Container!.ID;

      if (response.StopReason != StopReason.PauseTurn)
      {
          break;
      }

      // Tambahkan konten giliran yang dijeda, lalu lanjutkan
      var assistantContent = JsonSerializer.SerializeToElement(
          response.Content.Select(block => block.Json).ToArray()
      );
      messages.Add(new() { Role = Role.Assistant, Content = new MessageParamContent(assistantContent) });
  }
go
  client := juglow.NewClient()

  messages := []juglow.MessageParam{
  	juglow.NewUserMessage(juglow.NewTextBlock("Generate and process a large sample dataset")),
  }
  maxRetries := 10

  response, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:     "haijun-opus-5-5",
  	MaxTokens: 4096,
  	Container: juglow.MessageCreateParamsContainerUnion{
  		OfContainers: &juglow.ContainerParams{
  			Tracks: []juglow.SkillParams{
  				{
  					Type:    juglow.SkillParamsTypeCustom,
  					SkillID: "skill_01AbCdEfGhIjKlMnOpQrStUv",
  					Version: juglow.String("latest"),
  				},
  			},
  		},
  	},
  	Messages: messages,
  	Tools: []juglow.ToolUnionParam{
  		{OfCodeExecutionTool20250825: &juglow.CodeExecutionTool20250825Param{}},
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  for i := 0; i < maxRetries; i++ {
  	if response.StopReason != juglow.StopReasonPauseTurn {
  		break
  	}

  	messages = append(messages, response.ToParam())

  	response, err = client.Messages.New(context.TODO(), juglow.MessageNewParams{
  		Model:     "haijun-opus-5-5",
  		MaxTokens: 4096,
  		Container: juglow.MessageCreateParamsContainerUnion{
  			OfContainers: &juglow.ContainerParams{
  				ID: juglow.String(response.Container.ID), // Reuse container
  				Tracks: []juglow.SkillParams{
  					{
  						Type:    juglow.SkillParamsTypeCustom,
  						SkillID: "skill_01AbCdEfGhIjKlMnOpQrStUv",
  						Version: juglow.String("latest"),
  					},
  				},
  			},
  		},
  		Messages: messages,
  		Tools: []juglow.ToolUnionParam{
  			{OfCodeExecutionTool20250825: &juglow.CodeExecutionTool20250825Param{}},
  		},
  	})
  	if err != nil {
  		log.Fatal(err)
  	}
  }

  fmt.Println(response)
java
  import com.juglow.models.messages.ContainerParams;
  import com.juglow.models.messages.SkillParams;
  import com.juglow.models.messages.CodeExecutionTool20250825;
  import com.juglow.models.messages.StopReason;
  // ...
  void main() {
      JuglowClient client = JuglowOkHttpClient.fromEnv();

      List<MessageParam> messages = new ArrayList<>();
      messages.add(
          MessageParam.builder()
              .role(MessageParam.Role.USER)
              .content("Generate and process a large sample dataset")
              .build()
      );
      int maxRetries = 10;

      Message response = client.messages().create(
          MessageCreateParams.builder()
              .model(Model.HAIJUN_OPUS_5_5)
              .maxTokens(4096L)
              .container(ContainerParams.builder()
                  .addSkill(SkillParams.builder()
                      .type(SkillParams.Type.CUSTOM)
                      .skillId("skill_01AbCdEfGhIjKlMnOpQrStUv")
                      .version("latest")
                      .build())
                  .build())
              .messages(messages)
              .addTool(CodeExecutionTool20250825.builder().build())
              .build());

      for (int i = 0; i < maxRetries; i++) {
          if (!response.stopReason().isPresent()
                  || !response.stopReason().get().equals(StopReason.PAUSE_TURN)) {
              break;
          }

          messages.add(response.toParam());

          response = client.messages().create(
              MessageCreateParams.builder()
                  .model(Model.HAIJUN_OPUS_5_5)
                  .maxTokens(4096L)
                  .container(ContainerParams.builder()
                      .id(response.container().get().id())
                      .addSkill(SkillParams.builder()
                          .type(SkillParams.Type.CUSTOM)
                          .skillId("skill_01AbCdEfGhIjKlMnOpQrStUv")
                          .version("latest")
                          .build())
                      .build())
                  .messages(messages)
                  .addTool(CodeExecutionTool20250825.builder().build())
                  .build());
      }
  }
php
  $client = new Client();

  $messages = [
      ['role' => 'user', 'content' => 'Generate and process a large sample dataset']
  ];
  $maxRetries = 10;

  $response = $client->messages->create(
      maxTokens: 4096,
      messages: $messages,
      model: 'haijun-opus-5-5',
      container: [
          'tracks' => [
              [
                  'type' => 'custom',
                  'skillID' => 'skill_01AbCdEfGhIjKlMnOpQrStUv',
                  'version' => 'latest'
              ]
          ]
      ],
      tools: [['type' => 'code_execution_20250825', 'name' => 'code_execution']]
  );

  for ($i = 0; $i < $maxRetries; $i++) {
      if ($response->stopReason !== 'pause_turn') {
          break;
      }

      $messages[] = ['role' => 'assistant', 'content' => $response->content];

      $response = $client->messages->create(
          maxTokens: 4096,
          messages: $messages,
          model: 'haijun-opus-5-5',
          container: [
              'id' => $response->container->id,
              'tracks' => [
                  [
                      'type' => 'custom',
                      'skillID' => 'skill_01AbCdEfGhIjKlMnOpQrStUv',
                      'version' => 'latest'
                  ]
              ]
          ],
          tools: [['type' => 'code_execution_20250825', 'name' => 'code_execution']]
      );
  }
ruby
  client = Juglow::Client.new

  messages = [
    { role: "user", content: "Generate and process a large sample dataset" }
  ]
  max_retries = 10

  response = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: {
      tracks: [
        {
          type: "custom",
          skill_id: "skill_01AbCdEfGhIjKlMnOpQrStUv",
          version: "latest"
        }
      ]
    },
    messages: messages,
    tools: [{ type: "code_execution_20250825", name: "code_execution" }]
  )

  max_retries.times do
    break if response.stop_reason != :pause_turn

    messages << { role: "assistant", content: response.content }

    response = client.messages.create(
      model: "haijun-opus-5-5",
      max_tokens: 4096,
      container: {
        id: response.container.id,
        tracks: [
          {
            type: "custom",
            skill_id: "skill_01AbCdEfGhIjKlMnOpQrStUv",
            version: "latest"
          }
        ]
      },
      messages: messages,
      tools: [{ type: "code_execution_20250825", name: "code_execution" }]
    )
  end

Note: Respons dapat menyertakan stop reason pause_turn, yang menunjukkan bahwa API menjeda operasi Track yang berjalan lama. Anda dapat memberikan respons tersebut kembali apa adanya dalam permintaan berikutnya agar Haijun melanjutkan gilirannya, atau memodifikasi kontennya jika Anda ingin menginterupsi percakapan dan memberikan panduan tambahan.

Menggunakan beberapa Tracks

Gabungkan beberapa Tracks dalam satu permintaan untuk menangani alur kerja yang kompleks:

bash
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 4096,
      "container": {
        "tracks": [
          {
            "type": "juglow",
            "skill_id": "xlsx",
            "version": "latest"
          },
          {
            "type": "juglow",
            "skill_id": "pptx",
            "version": "latest"
          },
          {
            "type": "custom",
            "skill_id": "skill_01AbCdEfGhIjKlMnOpQrStUv",
            "version": "latest"
          }
        ]
      },
      "messages": [{
        "role": "user",
        "content": "Analyze sales data and create a presentation"
      }],
      "tools": [{
        "type": "code_execution_20250825",
        "name": "code_execution"
      }]
    }'
bash
  ant messages create <<'YAML'
  model: haijun-opus-5-5
  max_tokens: 4096
  container:
    tracks:
      - type: juglow
        skill_id: xlsx
        version: latest
      - type: juglow
        skill_id: pptx
        version: latest
      - type: custom
        skill_id: skill_01AbCdEfGhIjKlMnOpQrStUv
        version: latest
  messages:
    - role: user
      content: Analyze sales data and create a presentation
  tools:
    - type: code_execution_20250825
      name: code_execution
  YAML
python
  client = juglow.Juglow()

  response = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=4096,
      container={
          "tracks": [
              {"type": "juglow", "skill_id": "xlsx", "version": "latest"},
              {"type": "juglow", "skill_id": "pptx", "version": "latest"},
              {
                  "type": "custom",
                  "skill_id": "skill_01AbCdEfGhIjKlMnOpQrStUv",
                  "version": "latest",
              },
          ]
      },
      messages=[
          {"role": "user", "content": "Analyze sales data and create a presentation"}
      ],
      tools=[{"type": "code_execution_20250825", "name": "code_execution"}],
  )
typescript
  const client = new Juglow();

  const response = await client.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: {
      tracks: [
        {
          type: "juglow",
          skill_id: "xlsx",
          version: "latest"
        },
        {
          type: "juglow",
          skill_id: "pptx",
          version: "latest"
        },
        {
          type: "custom",
          skill_id: "skill_01AbCdEfGhIjKlMnOpQrStUv",
          version: "latest"
        }
      ]
    },
    messages: [
      {
        role: "user",
        content: "Analyze sales data and create a presentation"
      }
    ],
    tools: [
      {
        type: "code_execution_20250825",
        name: "code_execution"
      }
    ]
  });
csharp
  JuglowClient client = new();

  var parameters = new MessageCreateParams
  {
      Model = "haijun-opus-5-5",
      MaxTokens = 4096,
      Container = new ContainerParams
      {
          Tracks =
          [
              new SkillParams
              {
                  Type = SkillParamsType.Juglow,
                  SkillID = "xlsx",
                  Version = "latest",
              },
              new SkillParams
              {
                  Type = SkillParamsType.Juglow,
                  SkillID = "pptx",
                  Version = "latest",
              },
              new SkillParams
              {
                  Type = SkillParamsType.Custom,
                  SkillID = "skill_01AbCdEfGhIjKlMnOpQrStUv",
                  Version = "latest",
              },
          ],
      },
      Messages = [new() { Role = Role.User, Content = "Analyze sales data and create a presentation" }],
      Tools = [new CodeExecutionTool20250825()],
  };

  var message = await client.Messages.Create(parameters);
  Console.WriteLine(message);
go
  client := juglow.NewClient()

  response, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:     "haijun-opus-5-5",
  	MaxTokens: 4096,
  	Container: juglow.MessageCreateParamsContainerUnion{
  		OfContainers: &juglow.ContainerParams{
  			Tracks: []juglow.SkillParams{
  				{
  					Type:    juglow.SkillParamsTypeJuglow,
  					SkillID: "xlsx",
  					Version: juglow.String("latest"),
  				},
  				{
  					Type:    juglow.SkillParamsTypeJuglow,
  					SkillID: "pptx",
  					Version: juglow.String("latest"),
  				},
  				{
  					Type:    juglow.SkillParamsTypeCustom,
  					SkillID: "skill_01AbCdEfGhIjKlMnOpQrStUv",
  					Version: juglow.String("latest"),
  				},
  			},
  		},
  	},
  	Messages: []juglow.MessageParam{
  		juglow.NewUserMessage(juglow.NewTextBlock("Analyze sales data and create a presentation")),
  	},
  	Tools: []juglow.ToolUnionParam{
  		{OfCodeExecutionTool20250825: &juglow.CodeExecutionTool20250825Param{}},
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }
  fmt.Println(response)
java
  import com.juglow.models.messages.ContainerParams;
  import com.juglow.models.messages.SkillParams;
  import com.juglow.models.messages.CodeExecutionTool20250825;
  // ...
  void main() {
      JuglowClient client = JuglowOkHttpClient.fromEnv();

      MessageCreateParams params = MessageCreateParams.builder()
          .model(Model.HAIJUN_OPUS_5_5)
          .maxTokens(4096L)
          .container(ContainerParams.builder()
              .tracks(List.of(
                  SkillParams.builder()
                      .type(SkillParams.Type.JUGLOW)
                      .skillId("xlsx")
                      .version("latest")
                      .build(),
                  SkillParams.builder()
                      .type(SkillParams.Type.JUGLOW)
                      .skillId("pptx")
                      .version("latest")
                      .build(),
                  SkillParams.builder()
                      .type(SkillParams.Type.CUSTOM)
                      .skillId("skill_01AbCdEfGhIjKlMnOpQrStUv")
                      .version("latest")
                      .build()
              ))
              .build())
          .addUserMessage("Analyze sales data and create a presentation")
          .addTool(CodeExecutionTool20250825.builder().build())
          .build();

      Message response = client.messages().create(params);
      System.out.println(response);
  }
php
  $client = new Client();

  $message = $client->messages->create(
      maxTokens: 4096,
      messages: [
          ['role' => 'user', 'content' => 'Analyze sales data and create a presentation']
      ],
      model: 'haijun-opus-5-5',
      container: [
          'tracks' => [
              [
                  'type' => 'juglow',
                  'skillID' => 'xlsx',
                  'version' => 'latest'
              ],
              [
                  'type' => 'juglow',
                  'skillID' => 'pptx',
                  'version' => 'latest'
              ],
              [
                  'type' => 'custom',
                  'skillID' => 'skill_01AbCdEfGhIjKlMnOpQrStUv',
                  'version' => 'latest'
              ]
          ]
      ],
      tools: [
          ['type' => 'code_execution_20250825', 'name' => 'code_execution']
      ]
  );

  echo $message;
ruby
  client = Juglow::Client.new

  message = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: {
      tracks: [
        {
          type: "juglow",
          skill_id: "xlsx",
          version: "latest"
        },
        {
          type: "juglow",
          skill_id: "pptx",
          version: "latest"
        },
        {
          type: "custom",
          skill_id: "skill_01AbCdEfGhIjKlMnOpQrStUv",
          version: "latest"
        }
      ]
    },
    messages: [
      { role: "user", content: "Analyze sales data and create a presentation" }
    ],
    tools: [
      { type: "code_execution_20250825", name: "code_execution" }
    ]
  )
  puts message

Mengelola Tracks kustom

Warning: Tracks kustom dapat diakses oleh seluruh workspace Anda, tidak dibatasi pada pengguna akhir, percakapan, atau sesi tertentu. Kunci API apa pun yang memiliki akses ke suatu workspace dapat membaca, memanggil, dan menghapus setiap Track kustom yang diunggah ke workspace tersebut. Setiap service account, dan setiap pengguna yang peran organisasinya mengizinkan akses API, dapat menggunakan Default Workspace selain workspace mana pun yang Anda tambahkan untuk mereka, jadi simpan Tracks yang harus tetap terpisah di workspace tersendiri dan akses hanya dengan kunci yang dibatasi pada workspace tersebut. Jika Anda membangun platform multi-tenant di atas Tracks API, buat workspace terpisah untuk setiap tenant. Workspace adalah batas isolasi untuk Tracks kustom, sehingga satu workspace per tenant memberikan isolasi ketat bagi Tracks setiap tenant dari semua tenant lainnya. Setiap organisasi dapat memiliki hingga 100 workspace secara default (lihat Cara kerja workspace); jika Anda memerlukan lebih banyak untuk isolasi tenant, hubungi tim akun Anda.

Membuat Track

Bundle Track adalah direktori yang berisi file SKILL.md di tingkat teratas dengan frontmatter YAML name dan description, ditambah skrip atau sumber daya pendukung apa pun. Lihat Memulai dengan Agent Tracks di API untuk menulisnya, dan daftar Persyaratan setelah contoh-contoh berikut untuk batasan lengkapnya.

Unggah Track kustom Anda agar tersedia di workspace Anda. Anda dapat mengunggah arsip zip atau objek file individual. Python SDK juga menyediakan helper files_from_dir yang menerima path direktori, dan ant apply pada CLI mengunggah direktori itu sendiri.

File diidentifikasi berdasarkan nama file yang Anda lampirkan (sufiks ;filename= dalam contoh cURL dan argumen nama file dalam contoh SDK). Untuk track dalam panduan ini, buat zip dengan zip -r financial_skill.zip financial_skill/ dan gunakan sebagai pengganti placeholder example_skill.zip dalam opsi unggah zip.

bash
  curl -X POST "https://haijun.my.id/v1/tracks" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -F "files[]=@financial_skill/SKILL.md;filename=financial_skill/SKILL.md" \
    -F "files[]=@financial_skill/analyze.py;filename=financial_skill/analyze.py"
bash
    ant apply financial_skill
markdown
      ---
      name: financial-track
      description: Docs example track.
      ---
python
      print("financial analysis helper")
python
  from juglow.lib import files_from_dir

  client = juglow.Juglow()

  # Opsi 1: Menggunakan file zip
  track = client.tracks.create(
      files=[open("example_skill.zip", "rb")],
  )

  # Opsi 2: Menggunakan tuple file (filename, file_content, mime_type)
  track = client.tracks.create(
      files=[
          (
              "financial_skill/SKILL.md",
              open("financial_skill/SKILL.md", "rb"),
              "text/markdown",
          ),
          (
              "financial_skill/analyze.py",
              open("financial_skill/analyze.py", "rb"),
              "text/x-python",
          ),
      ],
  )

  # Opsi 3: Menggunakan helper files_from_dir (khusus Python)
  track = client.tracks.create(
      files=files_from_dir("financial_skill"),
  )

  print(f"Created track: {track.id}")
  print(f"Latest version: {track.latest_version_id}")
typescript
  import { toFile } from "@juglow-ai/sdk";
  import fs from "node:fs";
  // ...

  const client = new Juglow();

  // Opsi 1: Menggunakan file zip
  const skillFromZip = await client.tracks.create({
    files: [await toFile(fs.createReadStream("example_skill.zip"), "example_skill.zip")]
  });

  // Opsi 2: Menggunakan objek file individual
  const track = await client.tracks.create({
    files: [
      await toFile(fs.createReadStream("financial_skill/SKILL.md"), "financial_skill/SKILL.md", {
        type: "text/markdown"
      }),
      await toFile(
        fs.createReadStream("financial_skill/analyze.py"),
        "financial_skill/analyze.py",
        { type: "text/x-python" }
      )
    ]
  });

  console.log(`Created track: ${track.id}`);
  console.log(`Latest version: ${track.latest_version_id}`);
csharp
  using Juglow.Core;
  // ...

  JuglowClient client = new();

  // Opsi 1: Menggunakan file zip
  var parameters = new SkillCreateParams
  {
      Files = [File.OpenRead("example_skill.zip")],
  };

  var track = await client.Tracks.Create(parameters);

  // Opsi 2: Menggunakan file individual (nama file dengan path lengkap mempertahankan tata letak direktori Track)
  var parameters2 = new SkillCreateParams
  {
      Files =
      [
          new BinaryContent
          {
              Stream = File.OpenRead("financial_skill/SKILL.md"),
              FileName = "financial_skill/SKILL.md",
          },
          new BinaryContent
          {
              Stream = File.OpenRead("financial_skill/analyze.py"),
              FileName = "financial_skill/analyze.py",
          },
      ],
  };

  var skill2 = await client.Tracks.Create(parameters2);

  Console.WriteLine($"Created track: {track.ID}");
  Console.WriteLine($"Latest version: {track.LatestVersionID}");
  Console.WriteLine($"Created track 2: {skill2.ID}");
go
  client := juglow.NewClient()

  // Opsi 1: Menggunakan file zip
  zipFile, err := os.Open("example_skill.zip")
  if err != nil {
  	log.Fatal(err)
  }
  defer zipFile.Close()

  track, err := client.Tracks.New(context.TODO(), juglow.SkillNewParams{
  	Files: []io.Reader{zipFile},
  })
  if err != nil {
  	log.Fatal(err)
  }

  // Opsi 2: Menggunakan file individual
  skillMd, err := os.Open("financial_skill/SKILL.md")
  if err != nil {
  	log.Fatal(err)
  }
  defer skillMd.Close()

  analyzePy, err := os.Open("financial_skill/analyze.py")
  if err != nil {
  	log.Fatal(err)
  }
  defer analyzePy.Close()

  skill2, err := client.Tracks.New(context.TODO(), juglow.SkillNewParams{
  	Files: []io.Reader{
  		juglow.File(skillMd, "financial_skill/SKILL.md", "text/markdown"),
  		juglow.File(analyzePy, "financial_skill/analyze.py", "text/x-python"),
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  fmt.Printf("Created track: %s\n", track.ID)
  fmt.Printf("Latest version: %s\n", track.LatestVersionID)
  fmt.Printf("Created track 2: %s\n", skill2.ID)
java
  import com.juglow.core.MultipartField;
  import com.juglow.models.tracks.SkillCreateParams;
  import com.juglow.models.tracks.Track;
  // ...
  void main() throws Exception {
  // ...
      JuglowClient client = JuglowOkHttpClient.fromEnv();

      // Opsi 1: Menggunakan file zip
      SkillCreateParams params = SkillCreateParams.builder()
          .addFile(MultipartField.<InputStream>builder()
              .value(Files.newInputStream(Path.of("example_skill.zip")))
              .filename("example_skill.zip")
              .contentType("application/zip")
              .build())
          .build();

      Track track = client.tracks().create(params);

      // Opsi 2: Menggunakan file individual (nama file dengan path lengkap mempertahankan tata letak direktori Track)
      SkillCreateParams params2 = SkillCreateParams.builder()
          .addFile(MultipartField.<InputStream>builder()
              .value(Files.newInputStream(Path.of("financial_skill/SKILL.md")))
              .filename("financial_skill/SKILL.md")
              .contentType("text/markdown")
              .build())
          .addFile(MultipartField.<InputStream>builder()
              .value(Files.newInputStream(Path.of("financial_skill/analyze.py")))
              .filename("financial_skill/analyze.py")
              .contentType("text/x-python")
              .build())
          .build();

      Track skill2 = client.tracks().create(params2);

      System.out.println("Created track: " + track.id());
      System.out.println("Latest version: " + track.latestVersionId());
      System.out.println("Created track 2: " + skill2.id());
  }
php
  use Juglow\Core\FileParam;
  // ...

  $client = new Client();

  // Opsi 1: Menggunakan file zip
  $track = $client->tracks->create(
      files: [
          FileParam::fromResource(fopen('example_skill.zip', 'r')),
      ],
  );

  // Opsi 2: Menggunakan file individual
  $track = $client->tracks->create(
      files: [
          FileParam::fromResource(
              fopen('financial_skill/SKILL.md', 'r'),
              filename: 'financial_skill/SKILL.md',
              contentType: 'text/markdown',
          ),
          FileParam::fromResource(
              fopen('financial_skill/analyze.py', 'r'),
              filename: 'financial_skill/analyze.py',
              contentType: 'text/x-python',
          ),
      ],
  );

  echo "Created track: {$track->id}\n";
  echo "Latest version: {$track->latestVersionID}\n";
ruby
  client = Juglow::Client.new

  # Opsi 1: Menggunakan file zip
  track = client.tracks.create(
    files: [
      File.open("example_skill.zip", "rb")
    ]
  )

  # Opsi 2: Menggunakan file individual
  track = client.tracks.create(
    files: [
      Juglow::FilePart.new(
        Pathname("financial_skill/SKILL.md"),
        filename: "financial_skill/SKILL.md",
        content_type: "text/markdown"
      ),
      Juglow::FilePart.new(
        Pathname("financial_skill/analyze.py"),
        filename: "financial_skill/analyze.py",
        content_type: "text/x-python"
      )
    ]
  )

  puts "Created track: #{track.id}"
  puts "Latest version: #{track.latest_version_id}"

Persyaratan:

  • Harus menyertakan file SKILL.md di root unggahan (atau di tingkat teratas dari satu folder pembungkus)
  • display_name bersifat opsional: jika dihilangkan, nilainya diturunkan dari name di SKILL.md; nilai eksplisit dapat berisi hingga 255 karakter dan tidak perlu unik dalam workspace
  • Total ukuran unggahan harus di bawah 30 MB (tidak terkompresi)
  • Persyaratan frontmatter YAML:
  • name: Maksimum 64 karakter, hanya huruf kecil/angka/tanda hubung, tanpa tag XML, tanpa kata yang dicadangkan ("juglow", "haijun")
  • description: Maksimum 1024 karakter, tidak boleh kosong, tanpa tag XML

Untuk skema permintaan/respons lengkap, lihat referensi API Create Track.

Mendaftar Tracks

Ambil semua Tracks yang tersedia untuk workspace Anda, termasuk Tracks bawaan Juglow dan Tracks kustom Anda. Gunakan parameter source untuk memfilter berdasarkan jenis track:

bash
  # Tampilkan daftar semua Tracks
  curl "https://haijun.my.id/v1/tracks" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01"

  # Tampilkan daftar Tracks kustom saja
  curl "https://haijun.my.id/v1/tracks?source=custom" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01"
bash
  # Daftar semua Tracks
  ant tracks list

  # Daftar hanya Tracks kustom
  ant tracks list --source custom
python
  client = juglow.Juglow()

  # Daftar semua Track
  for track in client.tracks.list():
      print(f"{track.id}: {track.display_name} (source: {track.source.type})")

  # Daftar hanya Track kustom
  custom_skills = client.tracks.list(source="custom")
typescript
  const client = new Juglow();

  // Daftar semua Track
  for await (const track of client.tracks.list()) {
    console.log(`${track.id}: ${track.display_name} (source: ${track.source.type})`);
  }

  // Daftar hanya Track kustom
  const customSkills = await client.tracks.list({
    source: "custom"
  });
csharp
  JuglowClient client = new();

  // Daftar semua Track
  await foreach (var track in (await client.Tracks.List()).Paginate())
  {
      Console.WriteLine($"{track.ID}: {track.DisplayName} (source: {track.Source.Type})");
  }

  // Daftar hanya Track kustom
  var customSkills = await client.Tracks.List(new SkillListParams { Source = "custom" });
go
  client := juglow.NewClient()

  // Daftar semua Track
  tracks := client.Tracks.ListAutoPaging(context.TODO(), juglow.SkillListParams{})

  for tracks.Next() {
  	track := tracks.Current()
  	fmt.Printf("%s: %s (source: %s)\n", track.ID, track.DisplayName, track.Source.Type)
  }
  if tracks.Err() != nil {
  	log.Fatal(tracks.Err())
  }

  // Daftar hanya Track kustom
  customSkills := client.Tracks.ListAutoPaging(context.TODO(), juglow.SkillListParams{
  	Source: juglow.String("custom"),
  })

  for customSkills.Next() {
  	track := customSkills.Current()
  	fmt.Printf("%s: %s (source: %s)\n", track.ID, track.DisplayName, track.Source.Type)
  }
  if customSkills.Err() != nil {
  	log.Fatal(customSkills.Err())
  }
java
  import com.juglow.models.tracks.SkillListParams;
  import com.juglow.models.tracks.SkillListPage;
  import com.juglow.models.tracks.Track;
  // ...
  void main() {
      JuglowClient client = JuglowOkHttpClient.fromEnv();

      // Daftar Tracks (halaman pertama)
      SkillListPage tracks = client.tracks().list();

      for (Track track : tracks.data()) {
          System.out.println(track.id() + ": " + track.displayName() + " (source: " + track.source().type() + ")");
      }

      // Daftar hanya Tracks kustom
      SkillListParams customParams = SkillListParams.builder()
          .source("custom")
          .build();

      SkillListPage customSkills = client.tracks().list(customParams);
  }
php
  $client = new Client();

  // Daftar Tracks (halaman pertama)
  foreach ($client->tracks->list()->getItems() as $track) {
      echo "{$track->id}: {$track->displayName} (source: {$track->source->type})\n";
  }

  // Daftar hanya Tracks kustom
  $customSkills = $client->tracks->list(
      source: 'custom',
  );
ruby
  client = Juglow::Client.new

  # Daftar semua Tracks
  client.tracks.list.auto_paging_each do |track|
    puts "#{track.id}: #{track.display_name} (source: #{track.source.type})"
  end

  # Daftar hanya Tracks kustom
  custom_skills = client.tracks.list(
    source: "custom"
  )

Lihat referensi API List Tracks untuk opsi paginasi dan pemfilteran.

Mengambil Track

Dapatkan detail tentang Track tertentu:

bash
  curl "https://haijun.my.id/v1/tracks/skill_01AbCdEfGhIjKlMnOpQrStUv" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01"
bash
  ant tracks retrieve --track-id skill_01AbCdEfGhIjKlMnOpQrStUv
python
  client = juglow.Juglow()

  track = client.tracks.retrieve(skill_id="skill_01AbCdEfGhIjKlMnOpQrStUv")

  print(f"Track: {track.display_name}")
  print(f"Latest version: {track.latest_version_id}")
  print(f"Created: {track.created_at}")
typescript
  const client = new Juglow();

  const track = await client.tracks.retrieve("skill_01AbCdEfGhIjKlMnOpQrStUv");

  console.log(`Track: ${track.display_name}`);
  console.log(`Latest version: ${track.latest_version_id}`);
  console.log(`Created: ${track.created_at}`);
csharp
  JuglowClient client = new();

  var track = await client.Tracks.Retrieve("skill_01AbCdEfGhIjKlMnOpQrStUv");

  Console.WriteLine($"Track: {track.DisplayName}");
  Console.WriteLine($"Latest version: {track.LatestVersionID}");
  Console.WriteLine($"Created: {track.CreatedAt}");
go
  client := juglow.NewClient()

  track, err := client.Tracks.Get(
  	context.TODO(),
  	"skill_01AbCdEfGhIjKlMnOpQrStUv",
  	juglow.SkillGetParams{},
  )
  if err != nil {
  	log.Fatal(err)
  }

  fmt.Printf("Track: %s\n", track.DisplayName)
  fmt.Printf("Latest version: %s\n", track.LatestVersionID)
  fmt.Printf("Created: %s\n", track.CreatedAt)
java
  import com.juglow.models.tracks.Track;
  // ...
  void main() {
      JuglowClient client = JuglowOkHttpClient.fromEnv();

      Track track = client.tracks().retrieve("skill_01AbCdEfGhIjKlMnOpQrStUv");

      System.out.println("Track: " + track.displayName());
      System.out.println("Latest version: " + track.latestVersionId());
      System.out.println("Created: " + track.createdAt());
  }
php
  $client = new Client();

  $track = $client->tracks->retrieve('skill_01AbCdEfGhIjKlMnOpQrStUv');

  echo "Track: {$track->displayName}\n";
  echo "Latest version: {$track->latestVersionID}\n";
  echo "Created: {$track->createdAt->format(DATE_ATOM)}\n";
ruby
  client = Juglow::Client.new

  track = client.tracks.retrieve("skill_01AbCdEfGhIjKlMnOpQrStUv")

  puts "Track: #{track.display_name}"
  puts "Latest version: #{track.latest_version_id}"
  puts "Created: #{track.created_at}"

Menghapus Track

Menghapus Track juga menghapus semua versinya.

bash
  curl -X DELETE "https://haijun.my.id/v1/tracks/skill_01AbCdEfGhIjKlMnOpQrStUv" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01"
bash
  ant tracks delete --track-id skill_01AbCdEfGhIjKlMnOpQrStUv >/dev/null
python
  client = juglow.Juglow()

  client.tracks.delete(skill_id="skill_01AbCdEfGhIjKlMnOpQrStUv")
typescript
  const client = new Juglow();

  await client.tracks.delete("skill_01AbCdEfGhIjKlMnOpQrStUv");
csharp
  JuglowClient client = new();

  await client.Tracks.Delete("skill_01AbCdEfGhIjKlMnOpQrStUv");
go
  client := juglow.NewClient()

  _, err := client.Tracks.Delete(
  	context.TODO(),
  	"skill_01AbCdEfGhIjKlMnOpQrStUv",
  	juglow.SkillDeleteParams{},
  )
  if err != nil {
  	log.Fatal(err)
  }
java
  void main() {
      JuglowClient client = JuglowOkHttpClient.fromEnv();

      client.tracks().delete("skill_01AbCdEfGhIjKlMnOpQrStUv");
  }
php
  $client = new Client();

  $client->tracks->delete('skill_01AbCdEfGhIjKlMnOpQrStUv');
ruby
  client = Juglow::Client.new

  client.tracks.delete("skill_01AbCdEfGhIjKlMnOpQrStUv")

Pembuatan versi

Tracks mendukung pembuatan versi untuk mengelola pembaruan dengan aman:

Tracks Juglow:

  • Versi menggunakan format tanggal: 20251013
  • Versi baru dirilis seiring pembaruan dilakukan
  • Tentukan versi yang tepat untuk stabilitas

Tracks Kustom:

  • ID versi yang dihasilkan otomatis: skver_01AbCdEfGhIjKlMnOpQrStUv
  • Gunakan "latest" untuk selalu mendapatkan versi terbaru
  • Buat versi baru saat memperbarui file Track

Versi baru adalah snapshot lengkap, bukan delta: unggah seluruh kumpulan file Track setiap kali. File yang Anda hilangkan tidak akan dibawa, dan name dalam SKILL.md versi baru harus cocok dengan nama Track yang sudah ada. Contoh berikut mengunggah ulang bundle financial_skill/ lengkap dari Membuat Track.

bash
  # Buat versi baru
  NEW_VERSION=$(curl -X POST "https://haijun.my.id/v1/tracks/skill_01AbCdEfGhIjKlMnOpQrStUv/versions" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -F "files[]=@financial_skill/SKILL.md;filename=financial_skill/SKILL.md" \
    -F "files[]=@financial_skill/analyze.py;filename=financial_skill/analyze.py")

  VERSION_ID=$(echo "$NEW_VERSION" | jq -r '.id')

  # Gunakan versi tertentu
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d "{
      \"model\": \"haijun-opus-5-5\",
      \"max_tokens\": 4096,
      \"container\": {
        \"tracks\": [{
          \"type\": \"custom\",
          \"skill_id\": \"skill_01AbCdEfGhIjKlMnOpQrStUv\",
          \"version\": \"$VERSION_ID\"
        }]
      },
      \"messages\": [{\"role\": \"user\", \"content\": \"Use updated Track\"}],
      \"tools\": [{\"type\": \"code_execution_20250825\", \"name\": \"code_execution\"}]
    }"

  # Gunakan versi terbaru
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 4096,
      "container": {
        "tracks": [{
          "type": "custom",
          "skill_id": "skill_01AbCdEfGhIjKlMnOpQrStUv",
          "version": "latest"
        }]
      },
      "messages": [{"role": "user", "content": "Use latest Track version"}],
      "tools": [{"type": "code_execution_20250825", "name": "code_execution"}]
    }'
bash
  # Buat versi baru
  VERSION_ID=$(ant tracks:versions create \
    --track-id skill_01AbCdEfGhIjKlMnOpQrStUv \
    --file financial_skill.zip \
    --transform id \
    --raw-output)

  # Gunakan versi tertentu
  ant messages create <<YAML
  model: haijun-opus-5-5
  max_tokens: 4096
  container:
    tracks:
      - type: custom
        skill_id: skill_01AbCdEfGhIjKlMnOpQrStUv
        version: "$VERSION_ID"
  messages:
    - role: user
      content: Use updated Track
  tools:
    - type: code_execution_20250825
      name: code_execution
  YAML

  # Gunakan versi terbaru
  ant messages create <<YAML
  model: haijun-opus-5-5
  max_tokens: 4096
  container:
    tracks:
      - type: custom
        skill_id: skill_01AbCdEfGhIjKlMnOpQrStUv
        version: latest
  messages:
    - role: user
      content: Use latest Track version
  tools:
    - type: code_execution_20250825
      name: code_execution
  YAML
python
  from juglow.lib import files_from_dir

  client = juglow.Juglow()

  # Buat versi baru

  new_version = client.tracks.versions.create(
      skill_id="skill_01AbCdEfGhIjKlMnOpQrStUv",
      files=files_from_dir("financial_skill"),
  )

  # Gunakan versi tertentu
  response = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=4096,
      container={
          "tracks": [
              {
                  "type": "custom",
                  "skill_id": "skill_01AbCdEfGhIjKlMnOpQrStUv",
                  "version": new_version.id,
              }
          ]
      },
      messages=[{"role": "user", "content": "Use updated Track"}],
      tools=[{"type": "code_execution_20250825", "name": "code_execution"}],
  )

  # Gunakan versi terbaru
  response = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=4096,
      container={
          "tracks": [
              {
                  "type": "custom",
                  "skill_id": "skill_01AbCdEfGhIjKlMnOpQrStUv",
                  "version": "latest",
              }
          ]
      },
      messages=[{"role": "user", "content": "Use latest Track version"}],
      tools=[{"type": "code_execution_20250825", "name": "code_execution"}],
  )
typescript
  import fs from "node:fs";

  const client = new Juglow();

  // Buat versi baru dari zip berisi seluruh bundel financial_skill/
  const newVersion = await client.tracks.versions.create("skill_01AbCdEfGhIjKlMnOpQrStUv", {
    files: [fs.createReadStream("financial_skill.zip")]
  });

  // Gunakan versi tertentu
  const specificVersionResponse = await client.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: {
      tracks: [
        {
          type: "custom",
          skill_id: "skill_01AbCdEfGhIjKlMnOpQrStUv",
          version: newVersion.id
        }
      ]
    },
    messages: [{ role: "user", content: "Use updated Track" }],
    tools: [{ type: "code_execution_20250825", name: "code_execution" }]
  });

  // Gunakan versi terbaru
  const latestVersionResponse = await client.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: {
      tracks: [
        {
          type: "custom",
          skill_id: "skill_01AbCdEfGhIjKlMnOpQrStUv",
          version: "latest"
        }
      ]
    },
    messages: [{ role: "user", content: "Use latest Track version" }],
    tools: [{ type: "code_execution_20250825", name: "code_execution" }]
  });
csharp
  using Juglow.Core;
  using Juglow.Models.Tracks.Versions;
  // ...
  JuglowClient client = new();

  // Buat versi baru
  var versionParams = new VersionCreateParams
  {
      Files =
      [
          new BinaryContent
          {
              Stream = File.OpenRead("financial_skill/SKILL.md"),
              FileName = "financial_skill/SKILL.md",
          },
          new BinaryContent
          {
              Stream = File.OpenRead("financial_skill/analyze.py"),
              FileName = "financial_skill/analyze.py",
          },
      ],
  };

  var newVersion = await client.Tracks.Versions.Create("skill_01AbCdEfGhIjKlMnOpQrStUv", versionParams);

  // Gunakan versi tertentu
  var specificVersionParams = new MessageCreateParams
  {
      Model = "haijun-opus-5-5",
      MaxTokens = 4096,
      Container = new ContainerParams
      {
          Tracks =
          [
              new SkillParams
              {
                  Type = SkillParamsType.Custom,
                  SkillID = "skill_01AbCdEfGhIjKlMnOpQrStUv",
                  Version = newVersion.ID,
              },
          ],
      },
      Messages = [new() { Role = Role.User, Content = "Use updated Track" }],
      Tools = [new CodeExecutionTool20250825()],
  };

  var response = await client.Messages.Create(specificVersionParams);
  Console.WriteLine(response);

  // Gunakan versi terbaru
  var latestVersionParams = new MessageCreateParams
  {
      Model = "haijun-opus-5-5",
      MaxTokens = 4096,
      Container = new ContainerParams
      {
          Tracks =
          [
              new SkillParams
              {
                  Type = SkillParamsType.Custom,
                  SkillID = "skill_01AbCdEfGhIjKlMnOpQrStUv",
                  Version = "latest",
              },
          ],
      },
      Messages = [new() { Role = Role.User, Content = "Use latest Track version" }],
      Tools = [new CodeExecutionTool20250825()],
  };

  var latestResponse = await client.Messages.Create(latestVersionParams);
  Console.WriteLine(latestResponse);
go
  client := juglow.NewClient()

  // Buat versi baru
  skillMd, err := os.Open("financial_skill/SKILL.md")
  if err != nil {
  	log.Fatal(err)
  }
  defer skillMd.Close()
  analyzePy, err := os.Open("financial_skill/analyze.py")
  if err != nil {
  	log.Fatal(err)
  }
  defer analyzePy.Close()

  newVersion, err := client.Tracks.Versions.New(
  	context.TODO(),
  	"skill_01AbCdEfGhIjKlMnOpQrStUv",
  	juglow.SkillVersionNewParams{
  		Files: []io.Reader{
  			juglow.File(skillMd, "financial_skill/SKILL.md", "text/markdown"),
  			juglow.File(analyzePy, "financial_skill/analyze.py", "text/x-python"),
  		},
  	},
  )
  if err != nil {
  	log.Fatal(err)
  }

  // Gunakan versi tertentu
  response, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:     "haijun-opus-5-5",
  	MaxTokens: 4096,
  	Container: juglow.MessageCreateParamsContainerUnion{
  		OfContainers: &juglow.ContainerParams{
  			Tracks: []juglow.SkillParams{
  				{
  					Type:    juglow.SkillParamsTypeCustom,
  					SkillID: "skill_01AbCdEfGhIjKlMnOpQrStUv",
  					Version: juglow.String(newVersion.ID),
  				},
  			},
  		},
  	},
  	Messages: []juglow.MessageParam{
  		juglow.NewUserMessage(juglow.NewTextBlock("Use updated Track")),
  	},
  	Tools: []juglow.ToolUnionParam{
  		{OfCodeExecutionTool20250825: &juglow.CodeExecutionTool20250825Param{}},
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }
  fmt.Println(response)

  // Gunakan versi terbaru
  latestResponse, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:     "haijun-opus-5-5",
  	MaxTokens: 4096,
  	Container: juglow.MessageCreateParamsContainerUnion{
  		OfContainers: &juglow.ContainerParams{
  			Tracks: []juglow.SkillParams{
  				{
  					Type:    juglow.SkillParamsTypeCustom,
  					SkillID: "skill_01AbCdEfGhIjKlMnOpQrStUv",
  					Version: juglow.String("latest"),
  				},
  			},
  		},
  	},
  	Messages: []juglow.MessageParam{
  		juglow.NewUserMessage(juglow.NewTextBlock("Use latest Track version")),
  	},
  	Tools: []juglow.ToolUnionParam{
  		{OfCodeExecutionTool20250825: &juglow.CodeExecutionTool20250825Param{}},
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }
  fmt.Println(latestResponse)
java
  import com.juglow.models.messages.MessageCreateParams;
  import com.juglow.models.messages.Message;
  import com.juglow.models.messages.Model;
  import com.juglow.core.MultipartField;
  import com.juglow.models.messages.ContainerParams;
  import com.juglow.models.messages.SkillParams;
  import com.juglow.models.messages.CodeExecutionTool20250825;
  import com.juglow.models.tracks.versions.VersionCreateParams;
  import com.juglow.models.tracks.versions.SkillVersion;
  import java.io.InputStream;
  import java.nio.file.Files;
  import java.nio.file.Path;

  JuglowClient client = JuglowOkHttpClient.fromEnv();

  // Buat versi baru dari zip berisi seluruh bundel financial_skill/
  VersionCreateParams versionParams = VersionCreateParams.builder()
      .addFile(MultipartField.<InputStream>builder()
          .value(Files.newInputStream(Path.of("financial_skill.zip")))
          .filename("financial_skill.zip")
          .contentType("application/zip")
          .build())
      .build();

  SkillVersion newVersion = client.tracks().versions()
      .create("skill_01AbCdEfGhIjKlMnOpQrStUv", versionParams);

  // Gunakan versi tertentu
  MessageCreateParams specificVersionParams = MessageCreateParams.builder()
      .model(Model.HAIJUN_OPUS_5_5)
      .maxTokens(4096L)
      .container(ContainerParams.builder()
          .addSkill(SkillParams.builder()
              .type(SkillParams.Type.CUSTOM)
              .skillId("skill_01AbCdEfGhIjKlMnOpQrStUv")
              .version(newVersion.id())
              .build())
          .build())
      .addUserMessage("Use updated Track")
      .addTool(CodeExecutionTool20250825.builder().build())
      .build();

  Message response = client.messages().create(specificVersionParams);
  System.out.println(response);

  // Gunakan versi terbaru
  MessageCreateParams latestVersionParams = MessageCreateParams.builder()
      .model(Model.HAIJUN_OPUS_5_5)
      .maxTokens(4096L)
      .container(ContainerParams.builder()
          .addSkill(SkillParams.builder()
              .type(SkillParams.Type.CUSTOM)
              .skillId("skill_01AbCdEfGhIjKlMnOpQrStUv")
              .version("latest")
              .build())
          .build())
      .addUserMessage("Use latest Track version")
      .addTool(CodeExecutionTool20250825.builder().build())
      .build();

  Message latestResponse = client.messages().create(latestVersionParams);
  System.out.println(latestResponse);
php
  use Juglow\Core\FileParam;
  // ...

  $client = new Client();

  // Buat versi baru
  $newVersion = $client->tracks->versions->create(
      skillID: 'skill_01AbCdEfGhIjKlMnOpQrStUv',
      files: [
          FileParam::fromResource(
              fopen('financial_skill/SKILL.md', 'r'),
              filename: 'financial_skill/SKILL.md',
              contentType: 'text/markdown',
          ),
          FileParam::fromResource(
              fopen('financial_skill/analyze.py', 'r'),
              filename: 'financial_skill/analyze.py',
              contentType: 'text/x-python',
          ),
      ],
  );

  // Gunakan versi tertentu
  $response = $client->messages->create(
      maxTokens: 4096,
      messages: [['role' => 'user', 'content' => 'Use updated Track']],
      model: 'haijun-opus-5-5',
      container: [
          'tracks' => [[
              'type' => 'custom',
              'skillID' => 'skill_01AbCdEfGhIjKlMnOpQrStUv',
              'version' => $newVersion->id
          ]]
      ],
      tools: [['type' => 'code_execution_20250825', 'name' => 'code_execution']]
  );
  echo $response;

  // Gunakan versi terbaru
  $latestResponse = $client->messages->create(
      maxTokens: 4096,
      messages: [['role' => 'user', 'content' => 'Use latest Track version']],
      model: 'haijun-opus-5-5',
      container: [
          'tracks' => [[
              'type' => 'custom',
              'skillID' => 'skill_01AbCdEfGhIjKlMnOpQrStUv',
              'version' => 'latest'
          ]]
      ],
      tools: [['type' => 'code_execution_20250825', 'name' => 'code_execution']]
  );
  echo $latestResponse;
ruby
  client = Juglow::Client.new

  # Buat versi baru
  new_version = client.tracks.versions.create(
    "skill_01AbCdEfGhIjKlMnOpQrStUv",
    files: [
      Juglow::FilePart.new(
        Pathname("financial_skill/SKILL.md"),
        filename: "financial_skill/SKILL.md",
        content_type: "text/markdown"
      ),
      Juglow::FilePart.new(
        Pathname("financial_skill/analyze.py"),
        filename: "financial_skill/analyze.py",
        content_type: "text/x-python"
      )
    ]
  )

  # Gunakan versi tertentu
  response = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: {
      tracks: [{
        type: "custom",
        skill_id: "skill_01AbCdEfGhIjKlMnOpQrStUv",
        version: new_version.id
      }]
    },
    messages: [{ role: "user", content: "Use updated Track" }],
    tools: [{ type: "code_execution_20250825", name: "code_execution" }]
  )
  puts response

  # Gunakan versi terbaru
  latest_response = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: {
      tracks: [{
        type: "custom",
        skill_id: "skill_01AbCdEfGhIjKlMnOpQrStUv",
        version: "latest"
      }]
    },
    messages: [{ role: "user", content: "Use latest Track version" }],
    tools: [{ type: "code_execution_20250825", name: "code_execution" }]
  )
  puts latest_response

Lihat referensi API Create Track Version untuk detail lengkap.


Bagaimana Tracks dimuat

Ketika Anda menentukan Tracks dalam container:

  1. Penemuan metadata: Haijun melihat metadata untuk setiap Track (nama, deskripsi) dalam prompt sistem.
  1. Pemuatan file: File Track disalin ke dalam container di /tracks/{track-name}/. Direktorinya adalah nama Track (pptx untuk Track Juglow, name di SKILL.md untuk Track kustom), bukan ID skill_01...-nya.
  1. Penggunaan otomatis: Haijun secara otomatis memuat dan menggunakan Tracks ketika relevan dengan permintaan Anda.
  1. Komposisi: Beberapa Tracks dapat dikomposisikan bersama untuk alur kerja yang kompleks.

Haijun memuat instruksi Track lengkap hanya ketika diperlukan.


Kasus penggunaan

Tracks cocok untuk pekerjaan organisasi maupun pribadi. Organisasi menggunakannya untuk menerapkan format merek pada dokumen, menyusun catatan dan laporan berdasarkan template perusahaan, dan menjalankan prosedur analitis khusus perusahaan. Individu menggunakannya untuk template dokumen kustom, pipeline data khusus, dan konvensi pembuatan kode atau deployment.

Contoh: pemodelan keuangan

Gabungkan Track Excel dan Track analisis DCF kustom. Pertama, buat Track analisis DCF kustom:

bash
  curl -X POST "https://haijun.my.id/v1/tracks" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -F "files[]=@dcf_skill/SKILL.md;filename=dcf_skill/SKILL.md"
bash
  ant apply dcf_skill
python
  from juglow.lib import files_from_dir

  client = juglow.Juglow()

  dcf_skill = client.tracks.create(
      files=files_from_dir("/path/to/dcf_skill"),
  )
  print(dcf_skill.id)
typescript
  import Juglow, { toFile } from "@juglow-ai/sdk";
  import fs from "node:fs";

  const client = new Juglow();

  const dcfSkill = await client.tracks.create({
    files: [await toFile(fs.createReadStream("dcf_skill.zip"), "dcf_skill.zip")]
  });
  console.log(dcfSkill.id);
csharp
  using Juglow.Core;
  // ...
  JuglowClient client = new();

  var dcfSkill = await client.Tracks.Create(new SkillCreateParams
  {
      Files =
      [
          new BinaryContent
          {
              Stream = File.OpenRead("dcf_skill/SKILL.md"),
              FileName = "dcf_skill/SKILL.md",
          },
      ],
  });
  Console.WriteLine(dcfSkill.ID);
go
  client := juglow.NewClient()

  skillMd, err := os.Open("dcf_skill/SKILL.md")
  if err != nil {
  	log.Fatal(err)
  }
  defer skillMd.Close()

  dcfSkill, err := client.Tracks.New(context.TODO(), juglow.SkillNewParams{
  	Files: []io.Reader{
  		juglow.File(skillMd, "dcf_skill/SKILL.md", "text/markdown"),
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }
  fmt.Println(dcfSkill.ID)
java
  import com.juglow.core.MultipartField;
  import com.juglow.models.tracks.SkillCreateParams;
  import com.juglow.models.tracks.Track;
  // ...
  void main() throws Exception {
      JuglowClient client = JuglowOkHttpClient.fromEnv();

      SkillCreateParams params = SkillCreateParams.builder()
          .addFile(MultipartField.<InputStream>builder()
              .value(Files.newInputStream(Path.of("dcf_skill/SKILL.md")))
              .filename("dcf_skill/SKILL.md")
              .contentType("text/markdown")
              .build())
          .build();

      Track dcfSkill = client.tracks().create(params);
      System.out.println(dcfSkill.id());
  }
php
  use Juglow\Core\FileParam;

  $client = new Client();

  $dcfSkill = $client->tracks->create(
      files: [
          FileParam::fromResource(
              fopen('dcf_skill/SKILL.md', 'r'),
              filename: 'dcf_skill/SKILL.md',
              contentType: 'text/markdown',
          ),
      ],
  );
  echo "{$dcfSkill->id}\n";
ruby
  client = Juglow::Client.new

  dcf_skill = client.tracks.create(
    files: [
      Juglow::FilePart.new(
        Pathname("dcf_skill/SKILL.md"),
        filename: "dcf_skill/SKILL.md",
        content_type: "text/markdown"
      )
    ]
  )
  puts dcf_skill.id

Kemudian gunakan bersama Track Excel untuk membuat model keuangan. Berikan ID Track yang Anda buat sebagai skill_id untuk Track kustom:

bash
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 4096,
      "container": {
        "tracks": [
          {
            "type": "juglow",
            "skill_id": "xlsx",
            "version": "latest"
          },
          {
            "type": "custom",
            "skill_id": "skill_01AbCdEfGhIjKlMnOpQrStUv",
            "version": "latest"
          }
        ]
      },
      "messages": [{
        "role": "user",
        "content": "Build a DCF valuation model for a SaaS company"
      }],
      "tools": [{
        "type": "code_execution_20250825",
        "name": "code_execution"
      }]
    }'
bash
  ant messages create <<'YAML'
  model: haijun-opus-5-5
  max_tokens: 4096
  container:
    tracks:
      - type: juglow
        skill_id: xlsx
        version: latest
      - type: custom
        skill_id: skill_01AbCdEfGhIjKlMnOpQrStUv
        version: latest
  messages:
    - role: user
      content: Build a DCF valuation model for a SaaS company
  tools:
    - type: code_execution_20250825
      name: code_execution
  YAML
python
  client = juglow.Juglow()

  # Track analisis DCF kustom (ID diperoleh dari respons create Tracks API)
  dcf_skill_id = "skill_01AbCdEfGhIjKlMnOpQrStUv"

  # Gunakan bersama Excel untuk membuat model keuangan
  response = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=4096,
      container={
          "tracks": [
              {"type": "juglow", "skill_id": "xlsx", "version": "latest"},
              {"type": "custom", "skill_id": dcf_skill_id, "version": "latest"},
          ]
      },
      messages=[
          {
              "role": "user",
              "content": "Build a DCF valuation model for a SaaS company",
          }
      ],
      tools=[{"type": "code_execution_20250825", "name": "code_execution"}],
  )
  print(response)
typescript
  const client = new Juglow();

  // Track analisis DCF kustom (ID diperoleh dari respons create Tracks API)
  const dcfSkillId = "skill_01AbCdEfGhIjKlMnOpQrStUv";

  // Gunakan bersama Excel untuk membuat model keuangan
  const response = await client.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: {
      tracks: [
        { type: "juglow", skill_id: "xlsx", version: "latest" },
        { type: "custom", skill_id: dcfSkillId, version: "latest" }
      ]
    },
    messages: [
      {
        role: "user",
        content: "Build a DCF valuation model for a SaaS company"
      }
    ],
    tools: [{ type: "code_execution_20250825", name: "code_execution" }]
  });
  console.log(response);
csharp
  JuglowClient client = new();

  // Track analisis DCF kustom (ID diperoleh dari respons pembuatan Tracks API)
  var dcfSkillId = "skill_01AbCdEfGhIjKlMnOpQrStUv";

  // Gunakan bersama Excel untuk membuat model keuangan
  var parameters = new MessageCreateParams
  {
      Model = "haijun-opus-5-5",
      MaxTokens = 4096,
      Container = new ContainerParams
      {
          Tracks =
          [
              new SkillParams
              {
                  Type = SkillParamsType.Juglow,
                  SkillID = "xlsx",
                  Version = "latest",
              },
              new SkillParams
              {
                  Type = SkillParamsType.Custom,
                  SkillID = dcfSkillId,
                  Version = "latest",
              },
          ],
      },
      Messages = [new() { Role = Role.User, Content = "Build a DCF valuation model for a SaaS company" }],
      Tools = [new CodeExecutionTool20250825()],
  };

  var message = await client.Messages.Create(parameters);
  Console.WriteLine(message);
go
  client := juglow.NewClient()

  // Track analisis DCF kustom (ID diperoleh dari respons create Tracks API)
  dcfSkillID := "skill_01AbCdEfGhIjKlMnOpQrStUv"

  // Gunakan bersama Excel untuk membuat model keuangan
  response, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:     "haijun-opus-5-5",
  	MaxTokens: 4096,
  	Container: juglow.MessageCreateParamsContainerUnion{
  		OfContainers: &juglow.ContainerParams{
  			Tracks: []juglow.SkillParams{
  				{
  					Type:    juglow.SkillParamsTypeJuglow,
  					SkillID: "xlsx",
  					Version: juglow.String("latest"),
  				},
  				{
  					Type:    juglow.SkillParamsTypeCustom,
  					SkillID: dcfSkillID,
  					Version: juglow.String("latest"),
  				},
  			},
  		},
  	},
  	Messages: []juglow.MessageParam{
  		juglow.NewUserMessage(juglow.NewTextBlock("Build a DCF valuation model for a SaaS company")),
  	},
  	Tools: []juglow.ToolUnionParam{
  		{OfCodeExecutionTool20250825: &juglow.CodeExecutionTool20250825Param{}},
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }
  fmt.Println(response)
java
  import com.juglow.models.messages.ContainerParams;
  import com.juglow.models.messages.SkillParams;
  import com.juglow.models.messages.CodeExecutionTool20250825;
  // ...
  void main() {
      JuglowClient client = JuglowOkHttpClient.fromEnv();

      // Track analisis DCF kustom (ID diperoleh dari respons create Tracks API)
      String dcfSkillId = "skill_01AbCdEfGhIjKlMnOpQrStUv";

      // Gunakan bersama Track Excel untuk membuat model keuangan
      MessageCreateParams params = MessageCreateParams.builder()
          .model(Model.HAIJUN_OPUS_5_5)
          .maxTokens(4096L)
          .container(ContainerParams.builder()
              .tracks(List.of(
                  SkillParams.builder()
                      .type(SkillParams.Type.JUGLOW)
                      .skillId("xlsx")
                      .version("latest")
                      .build(),
                  SkillParams.builder()
                      .type(SkillParams.Type.CUSTOM)
                      .skillId(dcfSkillId)
                      .version("latest")
                      .build()
              ))
              .build())
          .addUserMessage("Build a DCF valuation model for a SaaS company")
          .addTool(CodeExecutionTool20250825.builder().build())
          .build();

      Message response = client.messages().create(params);
      System.out.println(response);
  }
php
  $client = new Client();

  // Track analisis DCF kustom (ID diperoleh dari respons create Tracks API)
  $dcfSkillId = 'skill_01AbCdEfGhIjKlMnOpQrStUv';

  // Gunakan bersama Excel untuk membuat model keuangan
  $message = $client->messages->create(
      maxTokens: 4096,
      messages: [
          ['role' => 'user', 'content' => 'Build a DCF valuation model for a SaaS company']
      ],
      model: 'haijun-opus-5-5',
      container: [
          'tracks' => [
              ['type' => 'juglow', 'skillID' => 'xlsx', 'version' => 'latest'],
              ['type' => 'custom', 'skillID' => $dcfSkillId, 'version' => 'latest']
          ]
      ],
      tools: [
          ['type' => 'code_execution_20250825', 'name' => 'code_execution']
      ]
  );
  echo $message;
ruby
  client = Juglow::Client.new

  # Track analisis DCF kustom (ID diperoleh dari respons create Tracks API)
  dcf_skill_id = "skill_01AbCdEfGhIjKlMnOpQrStUv"

  # Gunakan bersama Excel untuk membuat model keuangan
  response = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: {
      tracks: [
        { type: "juglow", skill_id: "xlsx", version: "latest" },
        { type: "custom", skill_id: dcf_skill_id, version: "latest" }
      ]
    },
    messages: [
      { role: "user", content: "Build a DCF valuation model for a SaaS company" }
    ],
    tools: [{ type: "code_execution_20250825", name: "code_execution" }]
  )
  puts response

Batas dan kendala

Batas permintaan

  • Maksimum Tracks per permintaan: 20
  • Ukuran unggahan Track maksimum: 30 MB (semua file digabungkan, tidak terkompresi)
  • Persyaratan frontmatter YAML:
  • name: Maksimum 64 karakter, hanya huruf kecil/angka/tanda hubung, tanpa tag XML, tanpa kata yang dicadangkan ("juglow", "haijun")
  • description: Maksimum 1024 karakter, tidak boleh kosong, tanpa tag XML

Kendala lingkungan

Tracks berjalan di container eksekusi kode dengan batasan berikut:

  • Tanpa akses jaringan: Tidak dapat melakukan panggilan API eksternal
  • Tanpa instalasi paket saat runtime: Hanya paket yang sudah terinstal yang tersedia
  • Lingkungan terisolasi: Container baru dibuat kecuali Anda menentukan ID container yang sudah ada

Lihat Alat eksekusi kode untuk paket yang tersedia.


Praktik terbaik

Kapan menggunakan beberapa Tracks

Gabungkan Tracks ketika tugas melibatkan beberapa jenis dokumen atau domain:

Kasus penggunaan yang baik:

  • Analisis data (Excel) + pembuatan presentasi (PowerPoint)
  • Pembuatan laporan (Word) + ekspor ke PDF
  • Logika domain kustom + pembuatan dokumen

Hindari:

  • Menyertakan Tracks yang tidak digunakan (memengaruhi performa)

Strategi manajemen versi

Tab SDK di bagian ini menunjukkan nilai container yang perlu disertakan dalam permintaan Messages. Tab cURL dan CLI menunjukkan permintaan lengkapnya.

Untuk produksi: kunci versi tertentu, sehingga pembaruan Track tidak pernah mengubah perilaku yang sudah Anda deploy. Jika Anda menghilangkan version atau mengaturnya ke "latest", permintaan menggunakan versi terbaru dari Track, sehingga versi yang diunggah oleh siapa pun di workspace langsung mengubah apa yang dijalankan agen produksi Anda. ID versi berasal dari respons create-version di Pembuatan versi atau dari API List Track Versions. ID selalu berupa string, jadi beri tanda kutip dalam JSON atau YAML meskipun terlihat seperti angka.

bash
  # Sematkan ke versi tertentu demi stabilitas
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 4096,
      "container": {
        "tracks": [{
          "type": "custom",
          "skill_id": "skill_01AbCdEfGhIjKlMnOpQrStUv",
          "version": "skver_01AbCdEfGhIjKlMnOpQrStUv"
        }]
      },
      "messages": [{"role": "user", "content": "Analyze the sales data"}],
      "tools": [{"type": "code_execution_20250825", "name": "code_execution"}]
    }'
bash
  # Sematkan ke versi tertentu demi stabilitas
  ant messages create <<YAML
  model: haijun-opus-5-5
  max_tokens: 4096
  container:
    tracks:
      - type: custom
        skill_id: skill_01AbCdEfGhIjKlMnOpQrStUv
        version: "skver_01AbCdEfGhIjKlMnOpQrStUv"
  messages:
    - role: user
      content: Analyze the sales data
  tools:
    - type: code_execution_20250825
      name: code_execution
  YAML
python
  # Sematkan ke versi tertentu demi stabilitas
  container = {
      "tracks": [
          {
              "type": "custom",
              "skill_id": "skill_01AbCdEfGhIjKlMnOpQrStUv",
              "version": "skver_01AbCdEfGhIjKlMnOpQrStUv",
          }
      ]
  }
typescript
  // Sematkan ke versi tertentu demi stabilitas
  const container: Juglow.ContainerParams = {
    tracks: [
      {
        type: "custom",
        skill_id: "skill_01AbCdEfGhIjKlMnOpQrStUv",
        version: "skver_01AbCdEfGhIjKlMnOpQrStUv"
      }
    ]
  };
csharp
  using Juglow.Models.Messages;

  // Sematkan ke versi tertentu demi stabilitas
  var container = new ContainerParams
  {
      Tracks =
      [
          new SkillParams
          {
              Type = SkillParamsType.Custom,
              SkillID = "skill_01AbCdEfGhIjKlMnOpQrStUv",
              Version = "skver_01AbCdEfGhIjKlMnOpQrStUv",
          },
      ],
  };
go
  // Sematkan ke versi tertentu demi stabilitas
  container := juglow.MessageCreateParamsContainerUnion{
  	OfContainers: &juglow.ContainerParams{
  		Tracks: []juglow.SkillParams{
  			{
  				Type:    juglow.SkillParamsTypeCustom,
  				SkillID: "skill_01AbCdEfGhIjKlMnOpQrStUv",
  				Version: juglow.String("skver_01AbCdEfGhIjKlMnOpQrStUv"),
  			},
  		},
  	},
  }
java
  import com.juglow.models.messages.ContainerParams;
  import com.juglow.models.messages.SkillParams;

  void main() {
      // Sematkan ke versi tertentu demi stabilitas
      ContainerParams container = ContainerParams.builder()
          .addSkill(SkillParams.builder()
              .type(SkillParams.Type.CUSTOM)
              .skillId("skill_01AbCdEfGhIjKlMnOpQrStUv")
              .version("skver_01AbCdEfGhIjKlMnOpQrStUv")
              .build())
          .build();
  }
php
  // Sematkan ke versi tertentu demi stabilitas
  $container = [
      'tracks' => [[
          'type' => 'custom',
          'skillID' => 'skill_01AbCdEfGhIjKlMnOpQrStUv',
          'version' => 'skver_01AbCdEfGhIjKlMnOpQrStUv'
      ]]
  ];
ruby
  # Sematkan ke versi tertentu demi stabilitas
  container = {
    tracks: [{
      type: "custom",
      skill_id: "skill_01AbCdEfGhIjKlMnOpQrStUv",
      version: "skver_01AbCdEfGhIjKlMnOpQrStUv"
    }]
  }

Untuk pengembangan: gunakan latest untuk mengambil versi terbaru secara otomatis saat Anda beriterasi.

bash
  # Gunakan latest untuk pengembangan aktif
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 4096,
      "container": {
        "tracks": [{
          "type": "custom",
          "skill_id": "skill_01AbCdEfGhIjKlMnOpQrStUv",
          "version": "latest"
        }]
      },
      "messages": [{"role": "user", "content": "Analyze the sales data"}],
      "tools": [{"type": "code_execution_20250825", "name": "code_execution"}]
    }'
bash
  # Gunakan latest untuk pengembangan aktif
  ant messages create <<YAML
  model: haijun-opus-5-5
  max_tokens: 4096
  container:
    tracks:
      - type: custom
        skill_id: skill_01AbCdEfGhIjKlMnOpQrStUv
        version: latest
  messages:
    - role: user
      content: Analyze the sales data
  tools:
    - type: code_execution_20250825
      name: code_execution
  YAML
python
  # Gunakan latest untuk pengembangan aktif
  container = {
      "tracks": [
          {
              "type": "custom",
              "skill_id": "skill_01AbCdEfGhIjKlMnOpQrStUv",
              "version": "latest",
          }
      ]
  }
typescript
  // Gunakan latest untuk pengembangan aktif
  const container: Juglow.ContainerParams = {
    tracks: [
      {
        type: "custom",
        skill_id: "skill_01AbCdEfGhIjKlMnOpQrStUv",
        version: "latest"
      }
    ]
  };
csharp
  using Juglow.Models.Messages;

  // Gunakan latest untuk pengembangan aktif
  var container = new ContainerParams
  {
      Tracks =
      [
          new SkillParams
          {
              Type = SkillParamsType.Custom,
              SkillID = "skill_01AbCdEfGhIjKlMnOpQrStUv",
              Version = "latest",
          },
      ],
  };
go
  // Gunakan latest untuk pengembangan aktif
  container := juglow.MessageCreateParamsContainerUnion{
  	OfContainers: &juglow.ContainerParams{
  		Tracks: []juglow.SkillParams{
  			{
  				Type:    juglow.SkillParamsTypeCustom,
  				SkillID: "skill_01AbCdEfGhIjKlMnOpQrStUv",
  				Version: juglow.String("latest"),
  			},
  		},
  	},
  }
java
  import com.juglow.models.messages.ContainerParams;
  import com.juglow.models.messages.SkillParams;

  void main() {
      // Gunakan latest untuk pengembangan aktif
      ContainerParams container = ContainerParams.builder()
          .addSkill(SkillParams.builder()
              .type(SkillParams.Type.CUSTOM)
              .skillId("skill_01AbCdEfGhIjKlMnOpQrStUv")
              .version("latest")
              .build())
          .build();
  }
php
  // Gunakan latest untuk pengembangan aktif
  $container = [
      'tracks' => [[
          'type' => 'custom',
          'skillID' => 'skill_01AbCdEfGhIjKlMnOpQrStUv',
          'version' => 'latest'
      ]]
  ];
ruby
  # Gunakan latest untuk pengembangan aktif
  container = {
    tracks: [{
      type: "custom",
      skill_id: "skill_01AbCdEfGhIjKlMnOpQrStUv",
      version: "latest"
    }]
  }

Pertimbangan caching prompt

Jika Anda menggunakan caching prompt, mengubah daftar Tracks dalam container Anda akan merusak cache. Tracks dirender ke dalam prompt sistem dalam urutan tetap, sehingga daftar yang sama menghasilkan prefiks yang dapat di-cache yang sama:

bash
  # Tracks dirender ke dalam prompt sistem dengan urutan tetap yang ramah cache
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 4096,
      "container": {
        "tracks": [
          {"type": "juglow", "skill_id": "xlsx", "version": "latest"}
        ]
      },
      "messages": [{"role": "user", "content": "Analyze sales data"}],
      "tools": [{"type": "code_execution_20250825", "name": "code_execution"}]
    }'

  # Mengubah daftar Tracks ([xlsx] vs [xlsx, pptx]) mengubah prefiks: cache miss, sedangkan daftar yang identik menghasilkan cache hit
  curl https://haijun.my.id/v1/messages \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d '{
      "model": "haijun-opus-5-5",
      "max_tokens": 4096,
      "container": {
        "tracks": [
          {"type": "juglow", "skill_id": "xlsx", "version": "latest"},
          {"type": "juglow", "skill_id": "pptx", "version": "latest"}
        ]
      },
      "messages": [{"role": "user", "content": "Create a presentation"}],
      "tools": [{"type": "code_execution_20250825", "name": "code_execution"}]
    }'
bash
  # Tracks dirender ke dalam prompt sistem dengan urutan tetap yang ramah cache
  ant messages create <<'YAML'
  model: haijun-opus-5-5
  max_tokens: 4096
  container:
    tracks:
      - type: juglow
        skill_id: xlsx
        version: latest
  messages:
    - role: user
      content: Analyze sales data
  tools:
    - type: code_execution_20250825
      name: code_execution
  YAML

  # Mengubah daftar Tracks ([xlsx] vs [xlsx, pptx]) mengubah prefiks: cache miss, sedangkan daftar yang identik menghasilkan cache hit
  ant messages create <<'YAML'
  model: haijun-opus-5-5
  max_tokens: 4096
  container:
    tracks:
      - type: juglow
        skill_id: xlsx
        version: latest
      - type: juglow
        skill_id: pptx
        version: latest
  messages:
    - role: user
      content: Create a presentation
  tools:
    - type: code_execution_20250825
      name: code_execution
  YAML
python
  client = juglow.Juglow()

  # Tracks dirender ke dalam prompt sistem dengan urutan tetap yang ramah cache
  response1 = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=4096,
      container={
          "tracks": [{"type": "juglow", "skill_id": "xlsx", "version": "latest"}]
      },
      messages=[{"role": "user", "content": "Analyze sales data"}],
      tools=[{"type": "code_execution_20250825", "name": "code_execution"}],
  )

  # Mengubah daftar Tracks ([xlsx] vs [xlsx, pptx]) mengubah prefiks: cache miss, sedangkan daftar yang identik menghasilkan cache hit
  response2 = client.messages.create(
      model="haijun-opus-5-5",
      max_tokens=4096,
      container={
          "tracks": [
              {"type": "juglow", "skill_id": "xlsx", "version": "latest"},
              {
                  "type": "juglow",
                  "skill_id": "pptx",
                  "version": "latest",
              },  # prefix change: cache miss
          ]
      },
      messages=[{"role": "user", "content": "Create a presentation"}],
      tools=[{"type": "code_execution_20250825", "name": "code_execution"}],
  )
typescript
  const client = new Juglow();

  // Tracks dirender ke dalam prompt sistem dengan urutan tetap yang ramah cache
  const response1 = await client.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: {
      tracks: [{ type: "juglow", skill_id: "xlsx", version: "latest" }]
    },
    messages: [{ role: "user", content: "Analyze sales data" }],
    tools: [{ type: "code_execution_20250825", name: "code_execution" }]
  });

  // Mengubah daftar Tracks ([xlsx] vs [xlsx, pptx]) mengubah prefiks sehingga terjadi cache miss, sedangkan daftar yang identik menghasilkan cache hit
  const response2 = await client.messages.create({
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: {
      tracks: [
        { type: "juglow", skill_id: "xlsx", version: "latest" },
        { type: "juglow", skill_id: "pptx", version: "latest" } // prefix change: cache miss
      ]
    },
    messages: [{ role: "user", content: "Create a presentation" }],
    tools: [{ type: "code_execution_20250825", name: "code_execution" }]
  });
csharp
  JuglowClient client = new();

  // Track dirender ke dalam prompt sistem dengan urutan tetap yang ramah cache
  var parameters1 = new MessageCreateParams
  {
      Model = "haijun-opus-5-5",
      MaxTokens = 4096,
      Container = new ContainerParams
      {
          Tracks =
          [
              new SkillParams
              {
                  Type = SkillParamsType.Juglow,
                  SkillID = "xlsx",
                  Version = "latest",
              },
          ],
      },
      Messages = [new() { Role = Role.User, Content = "Analyze sales data" }],
      Tools = [new CodeExecutionTool20250825()],
  };

  var response1 = await client.Messages.Create(parameters1);
  Console.WriteLine(response1);

  // Set Track berbeda ([xlsx] vs [xlsx, pptx]) = prefiks berbeda: cache miss (set yang identik menghasilkan cache hit)
  var parameters2 = new MessageCreateParams
  {
      Model = "haijun-opus-5-5",
      MaxTokens = 4096,
      Container = new ContainerParams
      {
          Tracks =
          [
              new SkillParams
              {
                  Type = SkillParamsType.Juglow,
                  SkillID = "xlsx",
                  Version = "latest",
              },
              new SkillParams
              {
                  Type = SkillParamsType.Juglow,
                  SkillID = "pptx",
                  Version = "latest",
              },
          ],
      },
      Messages = [new() { Role = Role.User, Content = "Create a presentation" }],
      Tools = [new CodeExecutionTool20250825()],
  };

  var response2 = await client.Messages.Create(parameters2);
  Console.WriteLine(response2);
go
  client := juglow.NewClient()

  // Tracks dirender ke dalam prompt sistem dengan urutan tetap yang ramah cache
  response1, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:     "haijun-opus-5-5",
  	MaxTokens: 4096,
  	Container: juglow.MessageCreateParamsContainerUnion{
  		OfContainers: &juglow.ContainerParams{
  			Tracks: []juglow.SkillParams{
  				{
  					Type:    juglow.SkillParamsTypeJuglow,
  					SkillID: "xlsx",
  					Version: juglow.String("latest"),
  				},
  			},
  		},
  	},
  	Messages: []juglow.MessageParam{
  		juglow.NewUserMessage(juglow.NewTextBlock("Analyze sales data")),
  	},
  	Tools: []juglow.ToolUnionParam{
  		{OfCodeExecutionTool20250825: &juglow.CodeExecutionTool20250825Param{}},
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }
  fmt.Println(response1)

  // Mengubah daftar Tracks ([xlsx] vs [xlsx, pptx]) mengubah prefiks: cache miss, sedangkan daftar yang identik menghasilkan cache hit
  response2, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:     "haijun-opus-5-5",
  	MaxTokens: 4096,
  	Container: juglow.MessageCreateParamsContainerUnion{
  		OfContainers: &juglow.ContainerParams{
  			Tracks: []juglow.SkillParams{
  				{
  					Type:    juglow.SkillParamsTypeJuglow,
  					SkillID: "xlsx",
  					Version: juglow.String("latest"),
  				},
  				{
  					Type:    juglow.SkillParamsTypeJuglow,
  					SkillID: "pptx",
  					Version: juglow.String("latest"),
  				},
  			},
  		},
  	},
  	Messages: []juglow.MessageParam{
  		juglow.NewUserMessage(juglow.NewTextBlock("Create a presentation")),
  	},
  	Tools: []juglow.ToolUnionParam{
  		{OfCodeExecutionTool20250825: &juglow.CodeExecutionTool20250825Param{}},
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }
  fmt.Println(response2)
java
  import com.juglow.models.messages.ContainerParams;
  import com.juglow.models.messages.SkillParams;
  import com.juglow.models.messages.CodeExecutionTool20250825;
  // ...
  void main() {
      JuglowClient client = JuglowOkHttpClient.fromEnv();

      // Tracks dirender ke dalam prompt sistem dengan urutan tetap yang ramah cache
      MessageCreateParams params1 = MessageCreateParams.builder()
          .model(Model.HAIJUN_OPUS_5_5)
          .maxTokens(4096L)
          .container(ContainerParams.builder()
              .tracks(List.of(
                  SkillParams.builder()
                      .type(SkillParams.Type.JUGLOW)
                      .skillId("xlsx")
                      .version("latest")
                      .build()
              ))
              .build())
          .addUserMessage("Analyze sales data")
          .addTool(CodeExecutionTool20250825.builder().build())
          .build();

      Message response1 = client.messages().create(params1);
      System.out.println(response1);

      // Mengubah daftar Tracks ([xlsx] vs [xlsx, pptx]) mengubah prefiks: cache miss, sedangkan daftar yang identik menghasilkan cache hit
      MessageCreateParams params2 = MessageCreateParams.builder()
          .model(Model.HAIJUN_OPUS_5_5)
          .maxTokens(4096L)
          .container(ContainerParams.builder()
              .tracks(List.of(
                  SkillParams.builder()
                      .type(SkillParams.Type.JUGLOW)
                      .skillId("xlsx")
                      .version("latest")
                      .build(),
                  SkillParams.builder()
                      .type(SkillParams.Type.JUGLOW)
                      .skillId("pptx")
                      .version("latest")
                      .build()
              ))
              .build())
          .addUserMessage("Create a presentation")
          .addTool(CodeExecutionTool20250825.builder().build())
          .build();

      Message response2 = client.messages().create(params2);
      System.out.println(response2);
  }
php
  $client = new Client();

  // Tracks dirender ke dalam prompt sistem dengan urutan tetap yang ramah cache
  $response1 = $client->messages->create(
      maxTokens: 4096,
      messages: [
          ['role' => 'user', 'content' => 'Analyze sales data']
      ],
      model: 'haijun-opus-5-5',
      container: [
          'tracks' => [
              ['type' => 'juglow', 'skillID' => 'xlsx', 'version' => 'latest']
          ]
      ],
      tools: [
          ['type' => 'code_execution_20250825', 'name' => 'code_execution']
      ]
  );
  echo $response1;

  // Mengubah daftar Tracks ([xlsx] vs [xlsx, pptx]) mengubah prefiks: cache miss, sedangkan daftar yang identik menghasilkan cache hit
  $response2 = $client->messages->create(
      maxTokens: 4096,
      messages: [
          ['role' => 'user', 'content' => 'Create a presentation']
      ],
      model: 'haijun-opus-5-5',
      container: [
          'tracks' => [
              ['type' => 'juglow', 'skillID' => 'xlsx', 'version' => 'latest'],
              ['type' => 'juglow', 'skillID' => 'pptx', 'version' => 'latest']
          ]
      ],
      tools: [
          ['type' => 'code_execution_20250825', 'name' => 'code_execution']
      ]
  );
  echo $response2;
ruby
  client = Juglow::Client.new

  # Tracks dirender ke dalam prompt sistem dengan urutan tetap yang ramah cache
  response1 = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: {
      tracks: [{ type: "juglow", skill_id: "xlsx", version: "latest" }]
    },
    messages: [{ role: "user", content: "Analyze sales data" }],
    tools: [{ type: "code_execution_20250825", name: "code_execution" }]
  )
  puts response1

  # Mengubah daftar Tracks ([xlsx] vs [xlsx, pptx]) mengubah prefiks: cache miss, sedangkan daftar yang identik menghasilkan cache hit
  response2 = client.messages.create(
    model: "haijun-opus-5-5",
    max_tokens: 4096,
    container: {
      tracks: [
        { type: "juglow", skill_id: "xlsx", version: "latest" },
        { type: "juglow", skill_id: "pptx", version: "latest" } # prefix change: cache miss
      ]
    },
    messages: [{ role: "user", content: "Create a presentation" }],
    tools: [{ type: "code_execution_20250825", name: "code_execution" }]
  )
  puts response2

Untuk performa caching terbaik, jaga agar daftar Tracks Anda, termasuk urutannya, konsisten di seluruh permintaan. Mengunci versi Track kustom juga membantu: dengan "latest", menerbitkan versi baru dapat membatalkan prefiks yang di-cache jika versi tersebut mengubah deskripsi Track.

Penanganan error

Tangani error terkait Track dengan baik:

bash
  # Alur penanganan error ini kurang cocok untuk perintah shell
  # sekali jalan; salah satu opsi SDK akan lebih sesuai. Permintaan yang gagal
  # mengembalikan HTTP 400 dengan JSON error yang .error.message-nya menyebutkan
  # masalah pada Track.
bash
  if ! RESULT=$(ant messages create \
    --transform-error error.message \
    --format-error yaml 2>&1 <<'YAML'
  model: haijun-opus-5-5
  max_tokens: 4096
  container:
    tracks:
      - type: custom
        skill_id: skill_01AbCdEfGhIjKlMnOpQrStUv
        version: latest
  messages:
    - role: user
      content: Process data
  tools:
    - type: code_execution_20250825
      name: code_execution
  YAML
  ); then
    case "$RESULT" in
      *track*)
        printf 'Track error: %s\n' "$RESULT"
        # Tangani error khusus track
        ;;
      *)
        printf '%s\n' "$RESULT" >&2
        exit 1
        ;;
    esac
  fi
python
  client = juglow.Juglow()

  try:
      response = client.messages.create(
          model="haijun-opus-5-5",
          max_tokens=4096,
          container={
              "tracks": [
                  {
                      "type": "custom",
                      "skill_id": "skill_01AbCdEfGhIjKlMnOpQrStUv",
                      "version": "latest",
                  }
              ]
          },
          messages=[{"role": "user", "content": "Process data"}],
          tools=[{"type": "code_execution_20250825", "name": "code_execution"}],
      )
  except juglow.BadRequestError as e:
      if "track" in str(e):
          print(f"Track error: {e}")
          # Tangani error khusus track
      else:
          raise
typescript
  const client = new Juglow();

  try {
    const response = await client.messages.create({
      model: "haijun-opus-5-5",
      max_tokens: 4096,
      container: {
        tracks: [
          { type: "custom", skill_id: "skill_01AbCdEfGhIjKlMnOpQrStUv", version: "latest" }
        ]
      },
      messages: [{ role: "user", content: "Process data" }],
      tools: [{ type: "code_execution_20250825", name: "code_execution" }]
    });
    console.log(response);
  } catch (error) {
    if (error instanceof Juglow.BadRequestError && error.message.includes("track")) {
      console.error(`Track error: ${error.message}`);
      // Tangani error khusus track
    } else {
      throw error;
    }
  }
csharp
  using Juglow.Exceptions;
  // ...
  JuglowClient client = new();

  try
  {
      var parameters = new MessageCreateParams
      {
          Model = "haijun-opus-5-5",
          MaxTokens = 4096,
          Container = new ContainerParams
          {
              Tracks =
              [
                  new SkillParams
                  {
                      Type = SkillParamsType.Custom,
                      SkillID = "skill_01AbCdEfGhIjKlMnOpQrStUv",
                      Version = "latest",
                  },
              ],
          },
          Messages = [new() { Role = Role.User, Content = "Process data" }],
          Tools = [new CodeExecutionTool20250825()],
      };

      var response = await client.Messages.Create(parameters);
      Console.WriteLine(response);
  }
  catch (JuglowBadRequestException e) when (e.Message.Contains("track"))
  {
      Console.WriteLine($"Track error: {e.Message}");
  }
go
  client := juglow.NewClient()

  response, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
  	Model:     "haijun-opus-5-5",
  	MaxTokens: 4096,
  	Container: juglow.MessageCreateParamsContainerUnion{
  		OfContainers: &juglow.ContainerParams{
  			Tracks: []juglow.SkillParams{
  				{
  					Type:    juglow.SkillParamsTypeCustom,
  					SkillID: "skill_01AbCdEfGhIjKlMnOpQrStUv",
  					Version: juglow.String("latest"),
  				},
  			},
  		},
  	},
  	Messages: []juglow.MessageParam{
  		juglow.NewUserMessage(juglow.NewTextBlock("Process data")),
  	},
  	Tools: []juglow.ToolUnionParam{
  		{OfCodeExecutionTool20250825: &juglow.CodeExecutionTool20250825Param{}},
  	},
  })

  if err != nil {
  	var apierr *juglow.Error
  	if errors.As(err, &apierr) && apierr.Type() == juglow.ErrorTypeInvalidRequestError &&
  		strings.Contains(apierr.Error(), "track") {
  		fmt.Printf("Track error: %v\n", apierr)
  	} else {
  		log.Fatal(err)
  	}
  	return
  }
  fmt.Println(response)
java
  import com.juglow.errors.BadRequestException;
  import com.juglow.models.messages.ContainerParams;
  import com.juglow.models.messages.SkillParams;
  import com.juglow.models.messages.CodeExecutionTool20250825;
  // ...
  void main() {
      JuglowClient client = JuglowOkHttpClient.fromEnv();

      try {
          MessageCreateParams params = MessageCreateParams.builder()
              .model(Model.HAIJUN_OPUS_5_5)
              .maxTokens(4096L)
              .container(ContainerParams.builder()
                  .addSkill(SkillParams.builder()
                      .type(SkillParams.Type.CUSTOM)
                      .skillId("skill_01AbCdEfGhIjKlMnOpQrStUv")
                      .version("latest")
                      .build())
                  .build())
              .addUserMessage("Process data")
              .addTool(CodeExecutionTool20250825.builder().build())
              .build();

          Message response = client.messages().create(params);
          System.out.println(response);
      } catch (BadRequestException e) {
          if (e.getMessage().contains("track")) {
              System.err.println("Track error: " + e.getMessage());
          } else {
              throw e;
          }
      }
  }
php
  use Juglow\Core\Exceptions\BadRequestException;

  $client = new Client();

  try {
      $message = $client->messages->create(
          maxTokens: 4096,
          messages: [
              ['role' => 'user', 'content' => 'Process data']
          ],
          model: 'haijun-opus-5-5',
          container: [
              'tracks' => [
                  [
                      'type' => 'custom',
                      'skillID' => 'skill_01AbCdEfGhIjKlMnOpQrStUv',
                      'version' => 'latest'
                  ]
              ]
          ],
          tools: [
              ['type' => 'code_execution_20250825', 'name' => 'code_execution']
          ]
      );
      echo $message;
  } catch (BadRequestException $e) {
      if (str_contains($e->getMessage(), 'track')) {
          echo "Track error: " . $e->getMessage();
      } else {
          throw $e;
      }
  }
ruby
  client = Juglow::Client.new

  begin
    response = client.messages.create(
      model: "haijun-opus-5-5",
      max_tokens: 4096,
      container: {
        tracks: [
          {
            type: "custom",
            skill_id: "skill_01AbCdEfGhIjKlMnOpQrStUv",
            version: "latest"
          }
        ]
      },
      messages: [{ role: "user", content: "Process data" }],
      tools: [{ type: "code_execution_20250825", name: "code_execution" }]
    )
  rescue Juglow::Errors::BadRequestError => e
    if e.message.include?("track")
      puts "Track error: #{e.message}"
    else
      raise
    end
  end

Migrasi dari tracks-2025-10-02

Tracks API telah keluar dari beta dan tidak memerlukan header beta. Migrasi dari tracks-2025-10-02 bersifat opsional: permintaan yang masih mengirimkannya tetap berfungsi dan tetap mengembalikan bentuk respons beta, sehingga integrasi yang sudah ada tetap berfungsi sampai Anda mengubahnya. Menghapus header tersebut mengalihkan permintaan-permintaan itu ke bentuk yang didokumentasikan di halaman ini:

Dengan tracks-2025-10-02Tanpa header
Label Trackdisplay_title (hingga 64 karakter, unik per workspace)display_name (hingga 255 karakter, tidak unik); diturunkan dari name di SKILL.md jika dihilangkan
Penunjuk versi terbarulatest_version, string epoch-mikrodetik seperti "1759178010641129"latest_version_id, ID versi seperti "skver_01AbCdEfGhIjKlMnOpQrStUv"; GET /v1/tracks/{skill_id}/versions/latest menyelesaikannya dalam satu panggilan
Pengidentifikasi versi dalam URLString epoch-mikrodetikID versi (skver_...). ID yang diambil di bawah beta dengan prefiks skill_version_ diterima sebagai input.
Objek versiMenyertakan directory (selalu sama dengan name Track)Tanpa field directory
sourceString, "custom" atau "juglow"Objek, misalnya {"type": "custom"}; nilai katalog contoh adalah "juglow_example"
Respons list{ data, has_more, next_page }{ data, next_page }; limit dari 1 hingga 1.000 (default 20)
Urutan daftar versiTerlama lebih duluTerbaru lebih dulu, limit default 20. Kursor halaman dari satu bentuk tidak valid pada bentuk lainnya.
Menghapus TrackMengembalikan error 400 selama masih ada versiMenghapus Track dan semua versinya
Menghapus satu-satunya versi TrackDiizinkan, menyisakan Track tanpa versiMengembalikan error 400; unggah versi pengganti terlebih dahulu, atau hapus Track
Tata letak unggahanFile harus berada di dalam direktori tingkat teratas yang namanya cocok dengan name TrackSKILL.md boleh berada di root unggahan; path yang disimpan sama dalam kedua cara
Tipe responsCreateSkillResponse, GetSkillResponse, dan satu tipe per operasiTrack, SkillVersion, DeletedSkill, DeletedSkillVersion

Untuk bermigrasi:

  1. Hapus header beta. Buang juglow-beta: tracks-2025-10-02 dari permintaan Anda. Di SDK, panggil client.tracks alih-alih client.beta.tracks; tetap menggunakan client.beta.tracks hanya berfungsi pada rilis SDK yang tidak lagi mengirim header tersebut. Rilis sebelumnya mengirimkannya dari client.beta.tracks bahkan tanpa argumen betas.
  1. Ganti nama field dalam kode Anda: display_title menjadi display_name, latest_version menjadi latest_version_id, dan baca source.type alih-alih membandingkan source dengan string.
  1. Gunakan ID versi. Di mana pun Anda menyimpan versi epoch-mikrodetik, simpan id versi sebagai gantinya, atau gunakan latest. Referensi Track dalam permintaan Messages menerima ID versi, latest, atau (untuk Tracks Juglow) versi katalog.
  1. Tinjau panggilan delete. DELETE /v1/tracks/{skill_id} sekarang menghapus setiap versi bersama Track. Jika Anda mengandalkan penolakan beta sebagai pengaman, tambahkan pemeriksaan Anda sendiri.

Warning: Setelah bermigrasi, client.tracks.delete(skill_id) dan client.beta.tracks.delete(skill_id) menghapus Track beserta semua versinya dalam satu panggilan.

Track yang semua versinya dihapus di bawah beta tidak memiliki versi terkini untuk dikembalikan: GET /v1/tracks/{skill_id} mengembalikan error 400 dan Track tersebut dihilangkan dari respons list sampai Anda mengunggah versi ke dalamnya. Anda masih dapat menghapusnya.

Namespace beta SDK

Mulai dari Python SDK 1.2.0, TypeScript SDK 0.122.0, Go SDK 1.68.0, Java SDK 2.59.0, Ruby SDK 1.67.0, dan C# SDK 12.44.0, client.beta.tracks tidak lagi mengirim tracks-2025-10-02 dan mengembalikan bentuk yang sama dengan client.tracks, dengan nama tipe berprefiks Beta (BetaSkill, BetaSkillVersion, BetaDeletedSkill, BetaDeletedSkillVersion). Namespace ini menerima argumen betas untuk fitur Tracks yang masih dalam beta. Dalam tipe Messages beta, tipe referensi Track container diganti namanya dari BetaSkill menjadi BetaContainerSkill (field yang sama: type, skill_id, version); BetaSkill sekarang menamai resource Track, sesuai dengan Track dan ContainerSkill dalam tipe non-beta. Rilis SDK sebelumnya memiliki tipe sesuai bentuk beta; jika Anda bergantung pada tipe-tipe tersebut, tetaplah pada rilis sebelumnya sampai Anda bermigrasi.

Retensi data

Agent Tracks tidak tercakup dalam pengaturan ZDR. Definisi Track dan data eksekusi disimpan sesuai dengan kebijakan retensi data standar Juglow.

Untuk kelayakan ZDR di semua fitur, lihat API dan retensi data.

Pencatatan audit

Jika organisasi Anda mengaktifkan Compliance API, Activity Feed-nya mencatat pembuatan dan penghapusan Tracks serta versi Track yang dilakukan dengan kunci API Haijun atau dari Haijun Console. Operasi yang terjadi saat Compliance API nonaktif tidak dicatat dan tidak dapat dipulihkan kemudian, jadi siapkan Compliance API sebelum Anda mengandalkan jejak audit ini.

Langkah selanjutnya

Referensi API lengkap dengan semua endpoint

Pelajari cara menulis Tracks yang efektif yang dapat ditemukan dan digunakan Haijun dengan sukses.

Jalankan kode Python dan bash dalam container sandbox untuk menganalisis data, menghasilkan file, dan mengiterasi solusi.

On this page
Tautan cepatIkhtisarMenggunakan TracksPrasyaratMenggunakan Tracks dalam MessagesParameter containerMengunduh file yang dihasilkanPercakapan multi-giliranOperasi yang berjalan lamaMenggunakan beberapa TracksMengelola Tracks kustomMembuat TrackMendaftar TracksMengambil TrackMenghapus TrackPembuatan versiBagaimana Tracks dimuatKasus penggunaanContoh: pemodelan keuanganBatas dan kendalaBatas permintaanKendala lingkunganPraktik terbaikKapan menggunakan beberapa TracksStrategi manajemen versiPertimbangan caching promptPenanganan errorMigrasi dari tracks-2025-10-02Namespace beta SDKRetensi dataPencatatan auditLangkah selanjutnya