Skip to content

Commit 7e071d0

Browse files
authored
Merge pull request #71 from legalfly-engineering/main
Create LEGALFLY Agent sample in Python
2 parents 7cccc85 + 5a8707a commit 7e071d0

File tree

4 files changed

+329
-0
lines changed

4 files changed

+329
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
PROJECT_ENDPOINT=
2+
MODEL=
3+
LEGALFLY_API_CONNECTION_NAME=
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# LEGALFLY
2+
3+
## Description
4+
5+
Legal insights grounded in trusted sources from your jurisdiction.
6+
7+
## Prerequisites
8+
9+
[Obtain an API key](https://www.legalfly.com/ai-foundry-agents) by filling in the request form. You'll receive the API key by e-mail within 1-2 working days.
10+
11+
## Setup
12+
13+
1. Go to [Azure AI Foundry portal](https://ai.azure.com/) and select your AI Project. Select **Management Center**.
14+
15+
1. Select **+new connection** in the settings page.
16+
1. Select **custom keys** in **other resource types**.
17+
18+
1. Enter the following information to create a connection to store your LEGALFLY key:
19+
1. Set **Custom keys** to "x-api-key", with the value being your LEGALFLY API key.
20+
1. Make sure **is secret** is checked.
21+
1. Set the connection name to your connection name. You use this connection name in your sample code or Foundry Portal later.
22+
1. For the **Access** setting, you can choose either _this project only_ or _shared to all projects_. Just make sure in your code, the connection string of the project you entered has access to this connection.
23+
24+
## Use LEGALFLY through a code-first experience
25+
26+
1. You can follow the [code sample](./legalfly.py) to use LEGALFLY through Agent SDK.
27+
1. Before running the sample:
28+
1. pip install azure-ai-agents azure-identity python-dotenv jsonref
29+
1. Set these environment variables with your own values:
30+
1. PROJECT_ENDPOINT - the Azure AI Agents connection string.
31+
1. MODEL - The deployment name of the AI model, as found under the "Name" column in the "Models + endpoints" tab in your Azure AI Foundry project.
32+
1. LEGALFLY_API_CONNECTION_NAME - The name of the connection for the LegalFly API.
33+
34+
## Customer Support Contact
35+
36+
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
{
2+
"openapi": "3.0.3",
3+
"servers": [
4+
{
5+
"url": "https://public-api.legalfly.com"
6+
}
7+
],
8+
"info": {
9+
"title": "LEGALFLY API documentation",
10+
"description": "Documentation for the public LEGALFLY API",
11+
"version": "0.0.1",
12+
"contact": {
13+
"name": "LEGALFLY",
14+
"url": "https://legalfly.com"
15+
}
16+
},
17+
"tags": [
18+
{
19+
"name": "Legal Counsel",
20+
"description": "Legal insights grounded by trusted sources from your jurisdiction."
21+
}
22+
],
23+
"components": {
24+
"securitySchemes": {
25+
"apiKeyAuth": {
26+
"type": "apiKey",
27+
"in": "header",
28+
"name": "x-api-key"
29+
}
30+
},
31+
"schemas": {
32+
"legal-counsel.body": {
33+
"type": "object",
34+
"properties": {
35+
"query": {
36+
"maxLength": 256,
37+
"type": "string"
38+
},
39+
"jurisdiction": {
40+
"enum": ["US", "UK", "BE", "NL", "FR"]
41+
}
42+
},
43+
"required": ["query"]
44+
},
45+
"legal-counsel.response": {
46+
"type": "object",
47+
"properties": {
48+
"message": {
49+
"type": "string"
50+
},
51+
"sources": {
52+
"type": "array",
53+
"items": {
54+
"type": "object",
55+
"properties": {
56+
"title": {
57+
"type": "string"
58+
},
59+
"url": {
60+
"type": "string"
61+
},
62+
"score": {
63+
"type": "number"
64+
}
65+
},
66+
"required": ["title", "url", "score"]
67+
}
68+
}
69+
},
70+
"required": ["message", "sources"]
71+
}
72+
}
73+
},
74+
"security": [
75+
{
76+
"apiKeyAuth": []
77+
}
78+
],
79+
"paths": {
80+
"/api/v1/legal-counsel": {
81+
"post": {
82+
"parameters": [],
83+
"responses": {
84+
"200": {
85+
"description": "Successful request.",
86+
"content": {
87+
"application/json": {
88+
"schema": {
89+
"$ref": "#/components/schemas/legal-counsel.response"
90+
}
91+
}
92+
}
93+
},
94+
"400": {
95+
"description": "Bad Request - Body validation failed."
96+
},
97+
"401": {
98+
"description": "Unauthorized - Missing or invalid API key."
99+
},
100+
"429": {
101+
"description": "Too Many Requests - Rate limit is hit."
102+
},
103+
"500": {
104+
"description": "Internal Server Error - Unexpected issue on server."
105+
}
106+
},
107+
"operationId": "getLegalCounsel",
108+
"summary": "Legal Counsel",
109+
"tags": ["Legal Counsel"],
110+
"requestBody": {
111+
"required": true,
112+
"content": {
113+
"application/json": {
114+
"schema": {
115+
"$ref": "#/components/schemas/legal-counsel.body"
116+
}
117+
},
118+
"multipart/form-data": {
119+
"schema": {
120+
"$ref": "#/components/schemas/legal-counsel.body"
121+
}
122+
},
123+
"text/plain": {
124+
"schema": {
125+
"$ref": "#/components/schemas/legal-counsel.body"
126+
}
127+
}
128+
}
129+
}
130+
}
131+
},
132+
"/health": {
133+
"get": {
134+
"operationId": "getHealth",
135+
"summary": "Health Check",
136+
"responses": {
137+
"200": {
138+
"description": "Successful request."
139+
}
140+
}
141+
}
142+
}
143+
}
144+
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# pylint: disable=line-too-long,useless-suppression
2+
# ------------------------------------
3+
# Copyright (c) Microsoft Corporation.
4+
# Licensed under the MIT License.
5+
# ------------------------------------
6+
7+
"""
8+
DESCRIPTION:
9+
This sample demonstrates how to use agent operations with the
10+
OpenAPI tool from the Azure Agents service using a synchronous client.
11+
To learn more about OpenAPI specs, visit https://learn.microsoft.com/openapi
12+
13+
USAGE:
14+
python legalfly.py
15+
16+
Before running the sample:
17+
18+
pip install azure-ai-projects azure-ai-agents azure-identity python-dotenv jsonref
19+
20+
Set these environment variables with your own values:
21+
1) PROJECT_ENDPOINT - the Azure AI Agents endpoint.
22+
2) MODEL - The deployment name of the AI model, as found under the "Name" column in
23+
the "Models + endpoints" tab in your Azure AI Foundry project.
24+
3) LEGALFLY_API_CONNECTION_NAME - The name of the connection for the LegalFly API.
25+
"""
26+
# <initialization>
27+
# Import necessary libraries
28+
import os
29+
import jsonref
30+
from azure.ai.projects import AIProjectClient
31+
from azure.identity import DefaultAzureCredential
32+
from azure.ai.agents.models import OpenApiTool, OpenApiConnectionAuthDetails, OpenApiConnectionSecurityScheme
33+
from dotenv import load_dotenv
34+
35+
load_dotenv()
36+
37+
# endpoint should be in the format "https://<your-ai-services-resource-name>.services.ai.azure.com/api/projects/<your-project-name>"
38+
endpoint = os.environ["PROJECT_ENDPOINT"]
39+
model = os.environ.get("MODEL", "gpt-4o")
40+
connection_name = os.environ["LEGALFLY_API_CONNECTION_NAME"]
41+
42+
43+
# Initialize the project client using the endpoint and default credentials
44+
with AIProjectClient.from_connection_string(
45+
conn_str=endpoint,
46+
credential=DefaultAzureCredential(exclude_interactive_browser_credential=False),
47+
) as project_client:
48+
# </initialization>
49+
50+
# Load the OpenAPI specification for the service from a local JSON file using jsonref to handle references
51+
with open("./legalfly.json", "r") as f:
52+
openapi_spec = jsonref.loads(f.read())
53+
54+
conn_id = project_client.connections.get(connection_name=connection_name).id
55+
# Create Auth object for the OpenApiTool (note that connection or managed identity auth setup requires additional setup in Azure)
56+
auth = OpenApiConnectionAuthDetails(security_scheme=OpenApiConnectionSecurityScheme(connection_id=conn_id))
57+
58+
59+
# Initialize the main OpenAPI tool definition for weather
60+
openapi_tool = OpenApiTool(
61+
name="getLegalCounsel",
62+
spec=openapi_spec,
63+
description="LegalFly legal counsel API",
64+
auth=auth
65+
)
66+
67+
# <agent_creation>
68+
# --- Agent Creation ---
69+
# Create an agent configured with the combined OpenAPI tool definitions
70+
agent = project_client.agents.create_agent(
71+
model=model, # Specify the model deployment
72+
name="my-agent", # Give the agent a name
73+
instructions="You are a helpful AI legal assistant. Act like a friendly person who possesses a lot of legal knowledge.",
74+
tools=openapi_tool.definitions, # Provide the list of tool definitions
75+
)
76+
print(f"Created agent, ID: {agent.id}")
77+
# </agent_creation>
78+
79+
# <thread_management>
80+
# --- Thread Management ---
81+
# Create a new conversation thread for the interaction
82+
thread = project_client.agents.create_thread()
83+
print(f"Created thread, ID: {thread.id}")
84+
85+
# Create the initial user message in the thread
86+
message = project_client.agents.create_message(
87+
thread_id=thread.id,
88+
role="user",
89+
# give an example of a user message that the agent can respond to
90+
content="What do I need to start a company in California?",
91+
)
92+
print(f"Created message, ID: {message.id}")
93+
# </thread_management>
94+
95+
# <message_processing>
96+
# --- Message Processing (Run Creation and Auto-processing) ---
97+
# Create and automatically process the run, handling tool calls internally
98+
# Note: This differs from the function_tool example where tool calls are handled manually
99+
run = project_client.agents.create_and_process_run(thread_id=thread.id, agent_id=agent.id)
100+
print(f"Run finished with status: {run.status}")
101+
# </message_processing>
102+
103+
# <tool_execution_loop> # Note: This section now processes completed steps, as create_and_process_run handles execution
104+
# --- Post-Run Step Analysis ---
105+
if run.status == "failed":
106+
print(f"Run failed: {run.last_error}")
107+
108+
# Retrieve the steps taken during the run for analysis
109+
run_steps = project_client.agents.list_run_steps(thread_id=thread.id, run_id=run.id)
110+
111+
# Loop through each step to display information
112+
for step in run_steps.data:
113+
print(f"Step {step['id']} status: {step['status']}")
114+
115+
# Check if there are tool calls recorded in the step details
116+
step_details = step.get("step_details", {})
117+
tool_calls = step_details.get("tool_calls", [])
118+
119+
if tool_calls:
120+
print(" Tool calls:")
121+
for call in tool_calls:
122+
print(f" Tool Call ID: {call.get('id')}")
123+
print(f" Type: {call.get('type')}")
124+
125+
function_details = call.get("function", {})
126+
if function_details:
127+
print(f" Function name: {function_details.get('name')}")
128+
print() # Add an extra newline between steps for readability
129+
# </tool_execution_loop>
130+
131+
# <cleanup>
132+
# --- Cleanup ---
133+
# Delete the agent resource to clean up
134+
project_client.agents.delete_agent(agent.id)
135+
print("Deleted agent")
136+
137+
# Fetch and log all messages exchanged during the conversation thread
138+
messages = project_client.agents.list_messages(thread_id=thread.id)
139+
print(f"Messages: {messages}")
140+
messages_array = messages.data
141+
for m in messages_array:
142+
content = m.get("content", [])
143+
if content and content[0].get("type") == "text":
144+
text_value = content[0].get("text", {}).get("value", "")
145+
print(f"Text: {text_value}")
146+
# </cleanup>

0 commit comments

Comments
 (0)