Skip to content

[Q] Seeking Clarification on History Behavior with StreamContentAsync and Function Calling #57

Description

@RyanStanLin

Hi team,

I've been working with the streaming function calling feature in ChatSession recently and have encountered some behavior regarding the History log that I found a bit confusing. I was hoping to ask for some clarification here to see if my understanding is correct or if this might be an unexpected behavior.

.NET SDK:

Version: 9.0.100-rc.2.24474.11
Commit: 315e1305db
Workload version: 9.0.100-manifests.f0b34084
MSBuild version: 17.12.0-preview-24473-03+fea15fbd1

Runtime Environment:

OS Name: Mac OS X
OS Version: 26.0
OS Platform: Darwin
RID: osx-arm64

My Use Case

I'm trying to implement a multi-turn conversation that involves function calls.

Use Demo JavascriptCodeExecutor as an example:

I replaced :

while (true)
{
    Console.WriteLine();
    Console.WriteLine("Input: ");
    var input = Console.ReadLine();
    if(input == "restart")
        goto restart;
    var prompt =
        $"\r\nExecute the javascript code for this task:\r\n\r\n{input}\r\n\r\n";
    
    var request = new GenerateContentRequest();
    request.AddText(input ?? "");
    
    //Generate Response
    var result = await chat.GenerateContentAsync(request).ConfigureAwait(false);
    
    //Print Result
    Console.WriteLine();
    Console.Write("Result from Gemini:\r\n");
    Console.WriteLine(result.Text());
}

With:

while (true)
{
    Console.WriteLine();
    Console.WriteLine("Input: ");
    var input = Console.ReadLine();
    if(input == "restart")
        goto restart;
    var prompt =
        $"\r\nExecute the javascript code for this task:\r\n\r\n{input}\r\n\r\n";
    
    var request = new GenerateContentRequest();
    request.AddText(input ?? "");
    
    //Generate Response Using Stream
    await foreach (var response in chat.StreamContentAsync(request, default))
    {
        if (!string.IsNullOrEmpty(response.Text()))
        {
            Console.Write(response.Text());
        }
    }
}

And I found the same issue

My general workflow is:

First Turn: I send a request that requires a tool call (e.g., "calculate the sum from 1 to 69").

My app receives the functionCall from the model, executes it locally, and sends the functionResponse back.

I then use StreamContentAsync to wait for the model to stream back the final text-based answer.

Second Turn: After the first turn is fully complete, I proceed to ask a follow-up question.

Observed Behavior

What I've noticed is that after the first turn concludes, when I'm about to start the second turn, the internal History of the ChatSession seems to only contain the last two parts of the first interaction: the functionResponse and the model's final text answer.

The conversation history in the JSON request is disorganized and illogical, which becomes particularly problematic when I have many callable functions and the model performs multiple function calls in a single response.

As far as I know, Gemini requires the first or last entry in the contents array to have a specific role; otherwise, it will throw an error.


Raw Json Request/Response

First Run Request 1: input "calculate the sum from 1 to 69"

{
  "contents": [
    {
      "parts": [
        {
          "text": "calculate the sum from 1 to 69"
        }
      ],
      "role": "user"
    }
  ],
  "tools": [
    {
      "functionDeclarations": [
        {
          "name": "ExecuteJavascriptCodeAsync",
...

First Run Response 1:

[
  {
    "candidates": [
      {
        "content": {
          "parts": [
            {
              "functionCall": {
                "name": "ExecuteJavascriptCodeAsync",
                "args": {
                  "code": "let sum = 0; for (let i = 1; i <= 69; i++) { sum += i; } console.log(sum);"
                }
              }
            }
          ],
          "role": "model"
        },
        "finishReason": "STOP"
      }
    ],
    "usageMetadata": {
      "promptTokenCount": 93,
      "candidatesTokenCount": 39,
      "totalTokenCount": 132,
      "promptTokensDetails": [
        {
          "modality": "TEXT",
          "tokenCount": 93
        }
      ],
      "candidatesTokensDetails": [
        {
          "modality": "TEXT",
          "tokenCount": 39
        }
      ]
    },
    "modelVersion": "gemini-2.0-flash",
    "responseId": "APd9aMzoJsuO7dcP9PjzmAY"
  }
]

First Run Request 2:

{
  "contents": [
    {
      "parts": [
        {
          "text": "calculate the sum from 1 to 69"
        }
      ],
      "role": "user"
    },
    {
      "parts": [
        {
          "functionCall": {
            "name": "ExecuteJavascriptCodeAsync",
            "args": {
              "code": "let sum = 0; for (let i = 1; i <= 69; i++) { sum += i; } console.log(sum);"
            }
          }
        }
      ],
      "role": "model"
    },
    {
      "parts": [
        {
          "functionResponse": {
            "name": "ExecuteJavascriptCodeAsync",
            "response": {
              "name": "ExecuteJavascriptCodeAsync",
              "content": "2415\n"
            }
          }
        }
      ],
      "role": "function"
    }
  ],
  "tools": [
    {
      "functionDeclarations": [
        {
          "name": "ExecuteJavascriptCodeAsync",
...

First Run Response 2:

[
  {
    "candidates": [
      {
        "content": {
          "parts": [
            {
              "text": "The"
            }
          ],
          "role": "model"
        }
      }
    ],
    "usageMetadata": {
      "promptTokenCount": 187,
      "totalTokenCount": 187,
      "promptTokensDetails": [
        {
          "modality": "TEXT",
          "tokenCount": 187
        }
      ]
    },
    "modelVersion": "gemini-2.0-flash",
    "responseId": "Avd9aPaoJdWU7dcPv9bKkQs"
  },
  {
    "candidates": [
      {
        "content": {
          "parts": [
            {
              "text": " sum from 1 to 69 is 2415.\n"
            }
          ],
          "role": "model"
        }
      }
    ],
    "usageMetadata": {
      "promptTokenCount": 187,
      "totalTokenCount": 187,
      "promptTokensDetails": [
        {
          "modality": "TEXT",
          "tokenCount": 187
        }
      ]
    },
    "modelVersion": "gemini-2.0-flash",
    "responseId": "Avd9aPaoJdWU7dcPv9bKkQs"
  },
  {
    "candidates": [
      {
        "content": {
          "parts": [
            {
              "text": ""
            }
          ],
          "role": "model"
        },
        "finishReason": "STOP"
      }
    ],
    "usageMetadata": {
      "promptTokenCount": 147,
      "candidatesTokenCount": 17,
      "totalTokenCount": 164,
      "promptTokensDetails": [
        {
          "modality": "TEXT",
          "tokenCount": 147
        }
      ],
      "candidatesTokensDetails": [
        {
          "modality": "TEXT",
          "tokenCount": 17
        }
      ]
    },
    "modelVersion": "gemini-2.0-flash",
    "responseId": "Avd9aPaoJdWU7dcPv9bKkQs"
  }
]

Second Run Request 1: input "calculate the sum from 1-42"

{
  "contents": [
    {
      "parts": [//Weird History
        {
          "functionResponse": {
            "name": "ExecuteJavascriptCodeAsync",
            "response": {
              "name": "ExecuteJavascriptCodeAsync",
              "content": "2415\n"
            }
          }
        }
      ],
      "role": "function"
    },
    {
      "parts": [
        {
          "text": "The\n sum from 1 to 69 is 2415.\n\n\n"
        }
      ],
      "role": "model"
    },
    {
      "parts": [
        {
          "text": "calculate the sum from 1 to 69"
        }
      ],
      "role": "user"
    },
    {
      "parts": [
        {
          "text": "\nThe\n sum from 1 to 69 is 2415.\n\n\n"
        }
      ],
      "role": "model"
    },
    {
      "parts": [
        {
          "text": "calculate the sum from 1 to 42"
        }
      ],
      "role": "user"
    }
  ],
  "tools": [
    {
      "functionDeclarations": [
        {
          "name": "ExecuteJavascriptCodeAsync",
...

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions