Spaces:
Sleeping
Sleeping
| # Copyright 2025 Akihito Miyazaki. team. All rights reserved. | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| import os | |
| import gradio as gr | |
| from smolagents import CodeAgent, tool | |
| from linear_api_utils import execute_query | |
| from sleep_per_last_token_model import SleepPerLastTokenModelLiteLLM | |
| # if use .env need these lines HF_TOKEN is optional | |
| """ | |
| LINEAR_API_KEY="lin_api_***" | |
| GROQ_API_KEY = "gsk_***" | |
| HF_TOKEN = "hf_***" | |
| """ | |
| def get_env_value(key, is_value_error_on_null=True): | |
| """ | |
| Gets an environment variable's value, loading from .env if needed. | |
| Args: | |
| key (str): Environment variable name. | |
| is_value_error_on_null (bool): Raise ValueError if not found (default: True). | |
| Returns: | |
| str: Environment variable value. | |
| Raises: | |
| ValueError: If `key` is not found and `is_value_error_on_null` is True. | |
| """ | |
| value = os.getenv(key) | |
| if value is None: | |
| from dotenv import load_dotenv | |
| load_dotenv() | |
| value = os.getenv(key) | |
| if is_value_error_on_null and value is None: | |
| raise ValueError(f"Need {key} on secret or .env(If running on local)") | |
| return value | |
| # SETTINGS | |
| LINEAR_ISSUE_LABEL = "huggingface-public" # only show issue with this label,I added for demo you can remove this | |
| ## set secret key on Space setting or .env(local) | |
| # hf_token = get_env_value("HF_TOKEN") | |
| groq_api_key = get_env_value("GROQ_API_KEY") | |
| api_key = get_env_value("LINEAR_API_KEY") | |
| if api_key is None: | |
| raise ValueError("Need LINEAR_API_KEY on secret") | |
| if groq_api_key is None: | |
| raise ValueError("Need GROQ_API_KEY on secret") | |
| model_id = "groq/llama3-8b-8192" | |
| def add_comment(issue_id, model_name, comment): | |
| """ | |
| Add comment to an issue. | |
| Args: | |
| issue_id (str): Issue ID. | |
| model_name (str): Model name added as title. | |
| comment (str): Comment text. | |
| Returns: | |
| str: query result json. | |
| """ | |
| comment = comment.replace('"', '\\"').replace("\n", "\\n") # escape doublequote | |
| # header = f"<!---\\n start-ai-comment({model_name}) \\n--->\\n" | |
| header = f"[ ](start-ai-comment:{model_name})\\n" | |
| header += f"# {model_name.split('/')[1]}'s comment'\\n" | |
| comment = header + comment | |
| comment_create_text = """ | |
| mutation CommentCreate { | |
| commentCreate( | |
| input: { | |
| issueId : "%s" | |
| body:"%s" | |
| } | |
| ) { | |
| success | |
| comment { | |
| id | |
| body | |
| } | |
| } | |
| }""" % (issue_id, comment) | |
| result = execute_query("add comment", comment_create_text, api_key) | |
| issue_id = None | |
| def change_state_reviewing(): | |
| """ | |
| Change the state of an issue to "Reviewing". | |
| Returns: | |
| None | |
| """ | |
| get_state_query_text = """ | |
| query Sate{ | |
| workflowStates(filter:{team:{id:{eq:"%s"}}}){ | |
| nodes{ | |
| id | |
| name | |
| } | |
| } | |
| } | |
| """ % (team_id) | |
| result = execute_query("State", get_state_query_text, api_key) | |
| state_id = None | |
| for state in result["data"]["workflowStates"]["nodes"]: | |
| if state["name"] == "Reviewing": | |
| state_id = state["id"] | |
| break | |
| if state_id is None: | |
| return | |
| issue_update_text = """ | |
| mutation IssueUpdate { | |
| issueUpdate( | |
| id: "%s", | |
| input: { | |
| stateId: "%s", | |
| } | |
| ) { | |
| success | |
| issue { | |
| id | |
| title | |
| state { | |
| id | |
| name | |
| } | |
| } | |
| } | |
| } | |
| """ % (issue_id, state_id) | |
| result = execute_query("IssueUpdate", issue_update_text, api_key) | |
| def get_todo_issue() -> str: | |
| """ | |
| Get the Todo issue. | |
| Returns: | |
| A string describing the current issue. | |
| """ | |
| global issue_id | |
| global issue_text | |
| priority_order = [1, 2, 3, 0, 4] | |
| for priority in priority_order: | |
| team_query_text = """ | |
| query Team { | |
| team(id: "%s") { | |
| id | |
| issues(first:1,filter:{ | |
| state:{ | |
| name:{ eq: "Todo" }, | |
| } | |
| priority:{eq:%d} | |
| }) { | |
| nodes { | |
| id | |
| title | |
| description | |
| createdAt | |
| } | |
| } | |
| } | |
| } | |
| """ % (team_id, priority) | |
| result = execute_query("Team", team_query_text, api_key, True) | |
| if len(result["data"]["team"]["issues"]["nodes"]) > 0: | |
| issue = result["data"]["team"]["issues"]["nodes"][0] | |
| issue_text = str(issue["title"]) | |
| issue_id = issue["id"] | |
| description = issue.get("description", None) | |
| if description is not None: | |
| issue_text += "\n" + description | |
| return issue_text | |
| return "Not Todo issue found" | |
| def generate_agent(): | |
| """ | |
| Generate an agent. | |
| Returns: | |
| An agent. | |
| """ | |
| model = SleepPerLastTokenModelLiteLLM( | |
| max_tokens=250, | |
| temperature=0.5, | |
| model_id=model_id, | |
| api_base="https://api.groq.com/openai/v1/", | |
| api_key=groq_api_key, | |
| ) | |
| agent = CodeAgent( | |
| model=model, | |
| tools=[get_todo_issue], ## add your tools here (don't remove final answer) | |
| max_steps=1, | |
| verbosity_level=1, | |
| grammar=None, | |
| planning_interval=None, | |
| name=None, | |
| description=None, | |
| ) | |
| return agent | |
| team_id = None | |
| def update_text(): | |
| """ | |
| Get the Todo issue and generate an agent. | |
| agent solve the issue and return text to Gradio outputs | |
| Returns: | |
| A string describing the current issue. | |
| A string describing the agent advice. | |
| """ | |
| def get_team_id(team_name): | |
| teams_text = """ | |
| query Teams { | |
| teams { | |
| nodes { | |
| id | |
| name | |
| } | |
| } | |
| } | |
| """ | |
| result = execute_query("Teams", teams_text, api_key) | |
| for team in result["data"]["teams"]["nodes"]: | |
| if team["name"] == team_name: | |
| return team["id"] | |
| return None | |
| team_name = "Agent" | |
| global team_id | |
| global issue_text | |
| team_id = get_team_id(team_name) | |
| if team_id is None: | |
| return f"Team {team_name} is not found", "Team not found" | |
| issue_text = "No Issue Found" | |
| agent_text = "No Agent Advice" | |
| agent = generate_agent() | |
| agent_text = agent.run( | |
| """ | |
| First, get the Todo using the get_todo tool. | |
| Then, solve the Todo. | |
| Finally, return the result of solving the Todo. | |
| """ | |
| ) | |
| # If you duplicate space uncomment below | |
| # add_comment(issue_id, model_id, agent_text) | |
| # change_state_reviewing() | |
| return issue_text, agent_text | |
| with gr.Blocks() as demo: | |
| gr.HTML(""" | |
| <h1>Initial API-Based Smolagents and Linear.app Integration Example</h1> | |
| <p>Large language models, like 70B parameter models, can often readily utilize tools such as <code>add_comment</code> or <code>change_state</code>, potentially handling multiple issues concurrently.</p> | |
| <p>However, smaller models may require repeated calls to a tool or even fail to utilize it entirely.</p> | |
| <p>Therefore, this initial example focuses on the <code>get_todo_issue()</code> tool.</p> | |
| <h2>Post-Duplication/Cloning Instructions</h2> | |
| <p>Need Linear.app acount and api key</a> | |
| <p>change script team name to your team name,add "Reviewing" State in your linear.app team setting<p> | |
| <p>comment out add_comment(),change_state_reviewing()</p> | |
| """) | |
| with gr.Row(): | |
| with gr.Column(): | |
| gr.Markdown("## Issue") | |
| # issue = gr.Markdown(load_text("issue.md")) | |
| issue = gr.Markdown("issue") | |
| with gr.Column(): | |
| gr.Markdown("## Agent advice(Don't trust them completely)") | |
| # output = gr.Markdown(load_text("output.md")) | |
| output = gr.Markdown("agent result") | |
| demo.load(update_text, inputs=None, outputs=[issue, output]) | |
| # for manual solve | |
| # bt = gr.Button("Next Todo") | |
| # bt.click(update_text, inputs=None, outputs=[issue, output]) | |
| if __name__ == "__main__": # without main call demo called twice | |
| demo.launch() | |