gemini-agent-framework 0.2.3__tar.gz → 0.2.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/PKG-INFO +1 -1
  2. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/docs/api_reference.md +2 -7
  3. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/docs/best_practices.md +2 -6
  4. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/docs/tutorials.md +3 -7
  5. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/pyproject.toml +1 -1
  6. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/src/gemini_agent/__init__.py +1 -1
  7. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/src/gemini_agent/agent.py +32 -91
  8. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/tests/test_agent.py +1 -19
  9. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/tests/test_class_methods.py +0 -21
  10. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/tests/test_variables.py +16 -18
  11. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/.flake8 +0 -0
  12. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/.github/workflows/ci.yml +0 -0
  13. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/.github/workflows/deploy-docs.yml +0 -0
  14. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/.github/workflows/docs.yml +0 -0
  15. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/.github/workflows/python-publish.yml +0 -0
  16. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/.github/workflows/tests.yml +0 -0
  17. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/.gitignore +0 -0
  18. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/CHANGELOG.md +0 -0
  19. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/CODE_OF_CONDUCT.md +0 -0
  20. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/CONTRIBUTING.md +0 -0
  21. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/LICENSE +0 -0
  22. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/README.md +0 -0
  23. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/docs/architecture.md +0 -0
  24. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/docs/index.md +0 -0
  25. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/docs/installation.md +0 -0
  26. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/mkdocs.yml +0 -0
  27. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/payload_variable_0.json +0 -0
  28. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/requirements.txt +0 -0
  29. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/tests/__init__.py +0 -0
  30. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/tests/formatting_payload_2.json +0 -0
  31. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/tests/payload_0.json +0 -0
  32. {gemini_agent_framework-0.2.3 → gemini_agent_framework-0.2.4}/tests/payload_1.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gemini-agent-framework
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Summary: A framework for building agents that use Gemini's function calling capabilities
5
5
  Project-URL: Homepage, https://github.com/m7mdony/gemini-agent-framework
6
6
  Project-URL: Documentation, https://m7mdony.github.io/gemini-agent-framework
@@ -120,15 +120,10 @@ The framework automatically maps Python types to Gemini JSON schema types:
120
120
 
121
121
  ## Response Structure
122
122
 
123
- The `response_structure` parameter in the `prompt` method allows you to define the expected structure of the response:
123
+ The `json_format` parameter in the `prompt` method allows you to define the expected structure with in the prompt:
124
124
 
125
125
  ```python
126
- response_structure = {
127
- "field_name": {
128
- "type": "string|number|boolean|array|object",
129
- "description": "Optional description"
130
- }
131
- }
126
+ Json_format = True
132
127
  ```
133
128
 
134
129
  ## Error Handling
@@ -120,14 +120,10 @@ agent.set_variable('count', 5, 'Counter')
120
120
 
121
121
  ### 1. Structured Responses
122
122
 
123
- Define clear response structures:
123
+ Define clear response structures within the prompt then apply them:
124
124
 
125
125
  ```python
126
- response_structure = {
127
- 'result': {'type': 'number', 'description': 'The calculation result'},
128
- 'steps': {'type': 'array', 'description': 'List of calculation steps'},
129
- 'explanation': {'type': 'string', 'description': 'Explanation of the calculation'}
130
- }
126
+ json_format = True
131
127
  ```
132
128
 
133
129
  ### 2. Error Responses
@@ -136,15 +136,11 @@ def safe_divide(a: float, b: float) -> float:
136
136
  ### 2. Using Response Structures
137
137
 
138
138
  ```python
139
- response_structure = {
140
- 'result': {'type': 'number', 'description': 'The calculation result'},
141
- 'steps': {'type': 'array', 'description': 'List of calculation steps'},
142
- 'explanation': {'type': 'string', 'description': 'Explanation of the calculation'}
143
- }
139
+
144
140
 
145
141
  response = agent.prompt(
146
- "Calculate 15 * 7 and show your work",
147
- response_structure=response_structure
142
+ """Calculate 15 * 7 and show your work respond with {"answer": number}""",
143
+ json_format=True
148
144
  )
149
145
  ```
150
146
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "gemini-agent-framework"
7
- version = "0.2.3"
7
+ version = "0.2.4"
8
8
  description = "A framework for building agents that use Gemini's function calling capabilities"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -1,4 +1,4 @@
1
1
  from .agent import Agent
2
2
 
3
- __version__ = "0.2.3"
3
+ __version__ = "0.2.4"
4
4
  __all__ = ["Agent"]
@@ -328,7 +328,7 @@ class Agent:
328
328
  self,
329
329
  user_prompt: str,
330
330
  system_prompt: Optional[str] = None,
331
- response_structure: Optional[Dict[str, Any]] = None,
331
+ json_format: bool = False,
332
332
  conversation_history: Optional[List[Dict[str, Any]]] = None,
333
333
  debug_scope: Optional[str] = [],
334
334
  ) -> Any:
@@ -338,15 +338,14 @@ class Agent:
338
338
  Args:
339
339
  user_prompt: The user's input prompt
340
340
  system_prompt: Optional system prompt to override the default
341
- response_structure: Optional structure to enforce on the response
341
+ json_format: If True, response will be formatted as JSON. Default is False (plain text)
342
342
  conversation_history: Optional list of previous conversation turns
343
343
 
344
344
  Returns:
345
- The model's response, processed according to the response structure if provided
345
+ The model's response, formatted as JSON if json_format is True, otherwise plain text
346
346
  """
347
347
  self._intermediate_results = {}
348
348
 
349
-
350
349
  current_contents = conversation_history if conversation_history else []
351
350
 
352
351
  # Add system instruction to payload
@@ -364,24 +363,10 @@ class Agent:
364
363
  payload["tools"] = [{"functionDeclarations": self._registered_tools_json}]
365
364
  payload["toolConfig"] = {"functionCallingConfig": {"mode": "AUTO"}}
366
365
 
367
- apply_structure_later = bool(response_structure) and bool(self._registered_tools_json)
368
- final_response_schema = None
369
- final_mime_type = None
370
-
371
- if response_structure and not self._registered_tools_json:
372
- apply_structure_later = False
373
- # If response_structure is a string type, make it more flexible
374
- if response_structure.get("type") == "string":
375
- response_structure = {
376
- "type": ["string", "object"],
377
- "properties": {"value": {"type": "string"}},
378
- }
379
- payload["generationConfig"] = {
380
- "response_mime_type": "application/json",
381
- "response_schema": response_structure,
382
- }
383
- final_mime_type = "application/json"
384
- final_response_schema = response_structure
366
+ # Don't set JSON formatting initially if tools are available
367
+ # We'll apply it later after tool calls are completed
368
+ apply_json_format_later = json_format and bool(self._registered_tools_json)
369
+
385
370
  count = 0
386
371
  while True:
387
372
  self._log_json(payload, f"payload_{count}.json", debug_scope)
@@ -410,8 +395,6 @@ class Agent:
410
395
  content = candidate["content"]
411
396
 
412
397
  for part in content["parts"]:
413
-
414
-
415
398
  if "functionCall" in part:
416
399
  payload["contents"].append({"role": "model", "parts": [part]})
417
400
  fc = part["functionCall"]
@@ -500,41 +483,32 @@ class Agent:
500
483
  elif "text" in part:
501
484
  final_text = part["text"]
502
485
 
503
- if final_mime_type == "application/json" and final_response_schema:
504
- try:
505
- structured_output = json.loads(final_text)
506
- if not any(
507
- "functionCall" in p
508
- for p in content["parts"][content["parts"].index(part) + 1 :]
509
- ):
510
- return structured_output
511
- except json.JSONDecodeError as e:
512
- self._log_text(
513
- f"Warning: Failed to parse initially structured output: {e}. Continuing with raw text.",
514
- debug_scope
515
- )
516
-
517
- elif apply_structure_later:
518
- if not any(
519
- "functionCall" in p
520
- for p in content["parts"][content["parts"].index(part) + 1 :]
521
- ):
522
- self._log_text("--- Attempting final structuring call ---", debug_scope)
523
- # Include the full conversation history in the formatting payload
486
+ # Check if there are more function calls coming
487
+ has_more_function_calls = any(
488
+ "functionCall" in p
489
+ for p in content["parts"][content["parts"].index(part) + 1 :]
490
+ )
491
+
492
+ if not has_more_function_calls:
493
+ # If JSON format is requested and we have tools, make a final formatting call
494
+ if apply_json_format_later:
495
+ self._log_text("--- Making final JSON formatting call ---", debug_scope)
524
496
  formatting_payload = {
497
+ "system_instruction": {
498
+ "parts": [{"text": system_prompt if system_prompt else ""},{"text": self._get_system_prompt()}]
499
+ },
525
500
  "contents": payload["contents"] + [
526
501
  {
527
502
  "role": "user",
528
503
  "parts": [
529
504
  {
530
- "text": f"Based on our conversation above, please format the following information according to the requested JSON structure:\n\n{final_text}"
505
+ "text": f"Based on our conversation above, please format your response as JSON. Here is the current response: {final_text}"
531
506
  }
532
507
  ],
533
508
  }
534
509
  ],
535
510
  "generationConfig": {
536
- "response_mime_type": "application/json",
537
- "response_schema": response_structure,
511
+ "response_mime_type": "application/json"
538
512
  },
539
513
  }
540
514
  self._log_json(formatting_payload, f"formatting_payload_{count}.json", debug_scope)
@@ -543,7 +517,7 @@ class Agent:
543
517
 
544
518
  if "error" in structured_response_data:
545
519
  self._log_text(
546
- f"Structuring call failed: {structured_response_data['error']}. Returning intermediate text.",
520
+ f"JSON formatting call failed: {structured_response_data['error']}. Returning raw text.",
547
521
  debug_scope
548
522
  )
549
523
  return final_text
@@ -556,57 +530,24 @@ class Agent:
556
530
  return structured_output
557
531
  except (KeyError, IndexError, json.JSONDecodeError) as e:
558
532
  self._log_text(
559
- f"Warning: Failed to parse structured output after formatting call: {e}. Returning intermediate text.",
533
+ f"Warning: Failed to parse JSON response after formatting call: {e}. Returning raw text.",
560
534
  debug_scope
561
535
  )
562
536
  return final_text
563
-
564
- elif not any(
565
- "functionCall" in p
566
- for p in content["parts"][content["parts"].index(part) + 1 :]
567
- ):
568
- if response_structure and not apply_structure_later:
569
- self._log_text("--- Attempting final structuring call ---", debug_scope)
570
- formatting_payload = {
571
- "contents": [
572
- {
573
- "role": "user",
574
- "parts": [
575
- {
576
- "text": f"Please format the following information according to the requested JSON structure:\n\n{final_text}"
577
- }
578
- ],
579
- }
580
- ],
581
- "generationConfig": {
582
- "response_mime_type": "application/json",
583
- "response_schema": response_structure,
584
- },
585
- }
586
- self._log_json(formatting_payload, f"formatting_payload_{count}.json", debug_scope)
587
- count += 1
588
- structured_response_data = self._call_gemini_api(formatting_payload, debug_scope)
589
-
590
- if "error" in structured_response_data:
591
- self._log_text(
592
- f"Structuring call failed: {structured_response_data['error']}. Returning intermediate text."
593
- , debug_scope
594
- )
595
- return final_text
596
-
537
+ elif json_format:
538
+ # Direct JSON formatting (no tools involved)
597
539
  try:
598
- structured_text = structured_response_data["candidates"][0][
599
- "content"
600
- ]["parts"][0]["text"]
601
- structured_output = json.loads(structured_text)
540
+ structured_output = json.loads(final_text)
602
541
  return structured_output
603
- except (KeyError, IndexError, json.JSONDecodeError) as e:
542
+ except json.JSONDecodeError as e:
604
543
  self._log_text(
605
- f"Warning: Failed to parse structured output after formatting call: {e}. Returning intermediate text.",
544
+ f"Warning: Failed to parse JSON response: {e}. Returning raw text.",
606
545
  debug_scope
607
546
  )
608
547
  return final_text
609
- return final_text
548
+ else:
549
+ # Return plain text response
550
+ return final_text
610
551
  continue
611
552
 
612
553
  except (KeyError, IndexError) as e:
@@ -64,25 +64,7 @@ response = agent_prompt_with_key_rotation(
64
64
  agent,
65
65
  user_prompt="multiply 3 and 7 then add 5 to the result",
66
66
  system_prompt="You are a helpful assistant. Give your response always with ❤️ at the start of the line. In your response you should mention the function you used.",
67
- response_structure={
68
- "type": "object",
69
- "properties": {
70
- "used_functions": {
71
- "type": "array",
72
- "items": {
73
- "type": "object",
74
- "properties": {
75
- "function_name": {"type": "string"},
76
- "parameters": {
77
- "type": "object",
78
- "properties": {"a": {"type": "integer"}, "b": {"type": "integer"}},
79
- },
80
- },
81
- },
82
- },
83
- "answer": {"type": "string"},
84
- },
85
- },
67
+
86
68
  )
87
69
 
88
70
  print(response)
@@ -39,27 +39,6 @@ def test_class_methods():
39
39
  # Test using class methods
40
40
  response = agent.prompt(
41
41
  "Multiply 5 with memory (starting at 0), then add 10 to the result",
42
- response_structure={
43
- "type": "object",
44
- "properties": {
45
- "used_functions": {
46
- "type": "array",
47
- "items": {
48
- "type": "object",
49
- "properties": {
50
- "function_name": {"type": "string"},
51
- "parameters": {
52
- "type": "object",
53
- "properties": {
54
- "number": {"type": "integer"}
55
- }
56
- }
57
- }
58
- }
59
- },
60
- "answer": {"type": "string"}
61
- }
62
- }
63
42
  )
64
43
  print(response)
65
44
 
@@ -107,16 +107,14 @@ print("\nExample 1: Count inputs in home page")
107
107
  response = agent.prompt(
108
108
 
109
109
  user_prompt="How many input tags are in the home page?",
110
- system_prompt="You are a tool that counts the number of input tags in an HTML page.",
111
- debug_scope=["json"],
112
- response_structure={
113
- "type": "object",
114
- "properties": {
115
- "count": {"type": "integer"},
116
- "input_types": {"type": "array", "items": {"type": "string"}}
117
- },
118
- "required": ["count", "input_types"]
119
- }
110
+ system_prompt=""""You are a tool that counts the number of input tags in an HTML page.
111
+ make the response in this format
112
+ {
113
+ "count": number,
114
+ "input_types": [input1,input2 ..... ] }
115
+
116
+ """,
117
+ json_format= True
120
118
  )
121
119
  print(json.dumps(response, indent=2))
122
120
 
@@ -124,13 +122,13 @@ print(json.dumps(response, indent=2))
124
122
  print("\nExample 2: Count inputs in login page")
125
123
  response = agent.prompt(
126
124
  "How many input tags are in this page "+ login_page,
127
- response_structure={
128
- "type": "object",
129
- "properties": {
130
- "count": {"type": "integer"},
131
- "input_types": {"type": "array", "items": {"type": "string"}}
132
- },
133
- "required": ["count", "input_types"]
134
- }
125
+ system_prompt="""
126
+ make the response in this format
127
+ {
128
+ "count": number,
129
+ "input_types": [input1,input2 ..... ] }
130
+
131
+ """,
132
+ json_format= True
135
133
  )
136
134
  print(json.dumps(response, indent=2))