update
Browse files- .gitignore +5 -0
- app.py +84 -42
- gradio_webhook_payload.py +13 -8
- gradio_webhook_server.py +34 -27
- linear_api_utils.py +3 -1
.gitignore
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__pycache__
|
| 2 |
+
.gradio
|
| 3 |
+
.env
|
| 4 |
+
webhook_header.json
|
| 5 |
+
webhook_request.json
|
app.py
CHANGED
|
@@ -1,21 +1,21 @@
|
|
| 1 |
"""
|
| 2 |
-
|
| 3 |
https://github.com/huggingface/huggingface_hub/blob/main/src/huggingface_hub/_webhooks_server.py
|
| 4 |
https://github.com/huggingface/huggingface_hub/blob/main/src/huggingface_hub/_webhooks_payload.py
|
| 5 |
|
| 6 |
-
|
| 7 |
https://studio.apollographql.com/public/Linear-API/variant/current/schema/reference/objects/Issue
|
| 8 |
|
| 9 |
-
|
| 10 |
|
| 11 |
-
|
| 12 |
-
target_webhook_label(
|
| 13 |
|
| 14 |
-
gradio,fastapi,pydantic
|
| 15 |
-
|
| 16 |
-
|
| 17 |
|
| 18 |
-
** Linear.app
|
| 19 |
# Copyright 2025-present, Akihito Miyazaki
|
| 20 |
#
|
| 21 |
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -30,7 +30,7 @@ gradio,fastapi,pydanticをあらかじめインストールしておく必要が
|
|
| 30 |
# See the License for the specific language governing permissions and
|
| 31 |
# limitations under the License.
|
| 32 |
|
| 33 |
-
** Hugging Face Hub
|
| 34 |
# Copyright 2023-present, the HuggingFace Inc. team.
|
| 35 |
#
|
| 36 |
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -45,8 +45,8 @@ gradio,fastapi,pydanticをあらかじめインストールしておく必要が
|
|
| 45 |
# See the License for the specific language governing permissions and
|
| 46 |
# limitations under the License.
|
| 47 |
|
| 48 |
-
|
| 49 |
-
|
| 50 |
"""
|
| 51 |
|
| 52 |
import os
|
|
@@ -60,6 +60,14 @@ from gradio_webhook_server import WebhooksServer
|
|
| 60 |
from gradio_webhook_payload import WebhookPayload
|
| 61 |
from sleep_per_last_token_model import SleepPerLastTokenModelLiteLLM
|
| 62 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
|
| 64 |
def get_env_value(key, is_value_error_on_null=True):
|
| 65 |
value = os.getenv(key)
|
|
@@ -109,13 +117,6 @@ for webhook in result["data"]["webhooks"]["nodes"]:
|
|
| 109 |
|
| 110 |
app = None
|
| 111 |
|
| 112 |
-
model = SleepPerLastTokenModelLiteLLM(
|
| 113 |
-
max_tokens=100,
|
| 114 |
-
temperature=0.5,
|
| 115 |
-
model_id="groq/llama3-8b-8192",
|
| 116 |
-
api_base="https://api.groq.com/openai/v1/",
|
| 117 |
-
api_key=groq_api_key,
|
| 118 |
-
)
|
| 119 |
|
| 120 |
"""
|
| 121 |
model = HfApiModel(
|
|
@@ -127,27 +128,32 @@ model = HfApiModel(
|
|
| 127 |
)
|
| 128 |
"""
|
| 129 |
|
| 130 |
-
agent = CodeAgent(
|
| 131 |
-
model=model,
|
| 132 |
-
tools=[], ## add your tools here (don't remove final answer)
|
| 133 |
-
max_steps=1,
|
| 134 |
-
verbosity_level=1,
|
| 135 |
-
grammar=None,
|
| 136 |
-
planning_interval=None,
|
| 137 |
-
name=None,
|
| 138 |
-
description=None,
|
| 139 |
-
)
|
| 140 |
-
|
| 141 |
|
| 142 |
def update():
|
| 143 |
-
result = agent.run(f"how to solve this issue:{app.text}")
|
| 144 |
-
return app.text, result
|
|
|
|
| 145 |
|
| 146 |
|
| 147 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
gr.HTML("""<h1>Linear.app Webhook Server</h1>
|
| 149 |
<p>This is Demo of Direct Webhook-triggered AIAgen</p>
|
| 150 |
-
<p>it's still just simple code,
|
| 151 |
<p><b>Imagine an agent, responding instantly.</b></p>
|
| 152 |
<p>Technically Gradio have no way to update without action<p>
|
| 153 |
<p></p><br>
|
|
@@ -156,20 +162,50 @@ with gr.Blocks() as ui:
|
|
| 156 |
<p>If you have any questions, please disable the Space or contact me before taking any action against my account. Thank you for your understanding.</p>
|
| 157 |
""")
|
| 158 |
with gr.Row():
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
|
| 164 |
app = WebhooksServer(
|
| 165 |
-
ui=
|
| 166 |
webhook_secret=webhook_key, # loaded by load_api_key
|
| 167 |
)
|
| 168 |
-
|
|
|
|
|
|
|
| 169 |
|
| 170 |
|
| 171 |
@app.add_webhook("/linear_webhook")
|
| 172 |
async def updated(payload: WebhookPayload):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
pprint(payload.dict(), indent=4)
|
| 174 |
|
| 175 |
data = payload.dict()["data"]
|
|
@@ -182,7 +218,12 @@ async def updated(payload: WebhookPayload):
|
|
| 182 |
|
| 183 |
if has_label:
|
| 184 |
text = data["description"]
|
| 185 |
-
app.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 186 |
return {"message": "ok"}
|
| 187 |
|
| 188 |
|
|
@@ -202,4 +243,5 @@ mutation {
|
|
| 202 |
result = execute_query("webhook_update", webhook_update_text, api_key)
|
| 203 |
|
| 204 |
|
| 205 |
-
|
|
|
|
|
|
| 1 |
"""
|
| 2 |
+
This code adapts the following code for Linear.app. It is not official code.
|
| 3 |
https://github.com/huggingface/huggingface_hub/blob/main/src/huggingface_hub/_webhooks_server.py
|
| 4 |
https://github.com/huggingface/huggingface_hub/blob/main/src/huggingface_hub/_webhooks_payload.py
|
| 5 |
|
| 6 |
+
Currently, it only supports a small subset of the Issue Object.
|
| 7 |
https://studio.apollographql.com/public/Linear-API/variant/current/schema/reference/objects/Issue
|
| 8 |
|
| 9 |
+
You need to set `api_key = linear-api-key` and `webhook_secret = linear-webhook-secret` in your `.env` file.
|
| 10 |
|
| 11 |
+
Since the local URL changes with each startup, the URL is updated in the Linear API at startup.
|
| 12 |
+
At startup, it overwrites the webhook with the label specified by `target_webhook_label` (default value: Gradio).
|
| 13 |
|
| 14 |
+
You need to pre-install gradio, fastapi, and pydantic.
|
| 15 |
+
You need to describe your Linear API key and Linear webhook secret in the `.env` file.
|
| 16 |
+
Also, since this example is only for Update, please create a Webhook with the label Gradio beforehand.
|
| 17 |
|
| 18 |
+
** Copyright Notice for Linear.app Adaptation **
|
| 19 |
# Copyright 2025-present, Akihito Miyazaki
|
| 20 |
#
|
| 21 |
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
| 30 |
# See the License for the specific language governing permissions and
|
| 31 |
# limitations under the License.
|
| 32 |
|
| 33 |
+
** License Notice for Hugging Face Hub Library **
|
| 34 |
# Copyright 2023-present, the HuggingFace Inc. team.
|
| 35 |
#
|
| 36 |
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
| 45 |
# See the License for the specific language governing permissions and
|
| 46 |
# limitations under the License.
|
| 47 |
|
| 48 |
+
This code includes parts of the Hugging Face Hub library, which is licensed under the Apache License, Version 2.0.
|
| 49 |
+
The full text of the license can be found at: http://www.apache.org/licenses/LICENSE-2.0
|
| 50 |
"""
|
| 51 |
|
| 52 |
import os
|
|
|
|
| 60 |
from gradio_webhook_payload import WebhookPayload
|
| 61 |
from sleep_per_last_token_model import SleepPerLastTokenModelLiteLLM
|
| 62 |
|
| 63 |
+
# .env
|
| 64 |
+
"""
|
| 65 |
+
LINEAR_API_KEY="lin_api_***"
|
| 66 |
+
HF_TOKEN = "hf_***"
|
| 67 |
+
LINEAR_WEBHOOK_KEY="lin_wh_***"
|
| 68 |
+
GROQ_API_KEY = "gsk_***"
|
| 69 |
+
"""
|
| 70 |
+
|
| 71 |
|
| 72 |
def get_env_value(key, is_value_error_on_null=True):
|
| 73 |
value = os.getenv(key)
|
|
|
|
| 117 |
|
| 118 |
app = None
|
| 119 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
|
| 121 |
"""
|
| 122 |
model = HfApiModel(
|
|
|
|
| 128 |
)
|
| 129 |
"""
|
| 130 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
|
| 132 |
def update():
|
| 133 |
+
# result = agent.run(f"how to solve this issue:{app.text}")
|
| 134 |
+
# return app.text, result
|
| 135 |
+
return "", ""
|
| 136 |
|
| 137 |
|
| 138 |
+
# still testing file or memory
|
| 139 |
+
def load_text(path):
|
| 140 |
+
with open(path, "r") as f:
|
| 141 |
+
return f.read()
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
def save_text(path, text):
|
| 145 |
+
with open(path, "w") as f:
|
| 146 |
+
f.write(text)
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
def update_text():
|
| 150 |
+
return app.issue, app.output
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
with gr.Blocks() as demo:
|
| 154 |
gr.HTML("""<h1>Linear.app Webhook Server</h1>
|
| 155 |
<p>This is Demo of Direct Webhook-triggered AIAgen</p>
|
| 156 |
+
<p>it's still just simple code,you have to reload when webhooked.</p>
|
| 157 |
<p><b>Imagine an agent, responding instantly.</b></p>
|
| 158 |
<p>Technically Gradio have no way to update without action<p>
|
| 159 |
<p></p><br>
|
|
|
|
| 162 |
<p>If you have any questions, please disable the Space or contact me before taking any action against my account. Thank you for your understanding.</p>
|
| 163 |
""")
|
| 164 |
with gr.Row():
|
| 165 |
+
with gr.Column():
|
| 166 |
+
gr.Markdown("## Issue")
|
| 167 |
+
# issue = gr.Markdown(load_text("issue.md"))
|
| 168 |
+
issue = gr.Markdown("issue")
|
| 169 |
+
with gr.Column():
|
| 170 |
+
gr.Markdown("## Agent advice(Don't trust them completely)")
|
| 171 |
+
# output = gr.Markdown(load_text("output.md"))
|
| 172 |
+
output = gr.Markdown("agent result")
|
| 173 |
+
demo.load(update_text, inputs=None, outputs=[issue, output])
|
| 174 |
+
|
| 175 |
+
# bt = gr.Button("Ask AI")
|
| 176 |
+
# bt.click(update, outputs=[issue_box, output_box])
|
| 177 |
|
| 178 |
app = WebhooksServer(
|
| 179 |
+
ui=demo,
|
| 180 |
webhook_secret=webhook_key, # loaded by load_api_key
|
| 181 |
)
|
| 182 |
+
|
| 183 |
+
app.output = "join course"
|
| 184 |
+
app.issue = "how to learn smolagent"
|
| 185 |
|
| 186 |
|
| 187 |
@app.add_webhook("/linear_webhook")
|
| 188 |
async def updated(payload: WebhookPayload):
|
| 189 |
+
def generate_agent():
|
| 190 |
+
model = SleepPerLastTokenModelLiteLLM(
|
| 191 |
+
max_tokens=250,
|
| 192 |
+
temperature=0.5,
|
| 193 |
+
model_id="groq/llama3-8b-8192",
|
| 194 |
+
api_base="https://api.groq.com/openai/v1/",
|
| 195 |
+
api_key=groq_api_key,
|
| 196 |
+
)
|
| 197 |
+
agent = CodeAgent(
|
| 198 |
+
model=model,
|
| 199 |
+
tools=[], ## add your tools here (don't remove final answer)
|
| 200 |
+
max_steps=1,
|
| 201 |
+
verbosity_level=1,
|
| 202 |
+
grammar=None,
|
| 203 |
+
planning_interval=None,
|
| 204 |
+
name=None,
|
| 205 |
+
description=None,
|
| 206 |
+
)
|
| 207 |
+
return agent
|
| 208 |
+
|
| 209 |
pprint(payload.dict(), indent=4)
|
| 210 |
|
| 211 |
data = payload.dict()["data"]
|
|
|
|
| 218 |
|
| 219 |
if has_label:
|
| 220 |
text = data["description"]
|
| 221 |
+
app.issue = text
|
| 222 |
+
# save_text("issue.md", text)
|
| 223 |
+
agent = generate_agent()
|
| 224 |
+
result = agent.run(f"how to solve this issue:{text}")
|
| 225 |
+
app.output = result
|
| 226 |
+
# save_text("output.md", result)
|
| 227 |
return {"message": "ok"}
|
| 228 |
|
| 229 |
|
|
|
|
| 243 |
result = execute_query("webhook_update", webhook_update_text, api_key)
|
| 244 |
|
| 245 |
|
| 246 |
+
if __name__ == "__main__": # without main call twice
|
| 247 |
+
app.launch(webhook_update=webhook_update)
|
gradio_webhook_payload.py
CHANGED
|
@@ -1,16 +1,21 @@
|
|
| 1 |
"""
|
| 2 |
-
|
|
|
|
| 3 |
https://github.com/huggingface/huggingface_hub/blob/main/src/huggingface_hub/_webhooks_payload.py
|
| 4 |
|
| 5 |
-
|
| 6 |
https://studio.apollographql.com/public/Linear-API/variant/current/schema/reference/objects/Issue
|
| 7 |
|
| 8 |
-
|
| 9 |
|
| 10 |
-
|
|
|
|
| 11 |
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
-
** Linear.app
|
| 14 |
# Copyright 2025-present, Akihito Miyazaki
|
| 15 |
#
|
| 16 |
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -25,7 +30,7 @@ Issueの新規・更新・削除は確認しました。
|
|
| 25 |
# See the License for the specific language governing permissions and
|
| 26 |
# limitations under the License.
|
| 27 |
|
| 28 |
-
** Hugging Face Hub
|
| 29 |
# Copyright 2023-present, the HuggingFace Inc. team.
|
| 30 |
#
|
| 31 |
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -40,8 +45,8 @@ Issueの新規・更新・削除は確認しました。
|
|
| 40 |
# See the License for the specific language governing permissions and
|
| 41 |
# limitations under the License.
|
| 42 |
|
| 43 |
-
|
| 44 |
-
|
| 45 |
"""
|
| 46 |
|
| 47 |
"""Contains data structures to parse the webhooks payload."""
|
|
|
|
| 1 |
"""
|
| 2 |
+
This code adapts the following code for Linear.app. It is not official code.
|
| 3 |
+
https://github.com/huggingface/huggingface_hub/blob/main/src/huggingface_hub/_webhooks_server.py
|
| 4 |
https://github.com/huggingface/huggingface_hub/blob/main/src/huggingface_hub/_webhooks_payload.py
|
| 5 |
|
| 6 |
+
Currently, it only supports a small subset of the Issue Object.
|
| 7 |
https://studio.apollographql.com/public/Linear-API/variant/current/schema/reference/objects/Issue
|
| 8 |
|
| 9 |
+
You need to set `api_key = linear-api-key` and `webhook_secret = linear-webhook-secret` in your `.env` file.
|
| 10 |
|
| 11 |
+
Since the local URL changes with each startup, the URL is updated in the Linear API at startup.
|
| 12 |
+
At startup, it overwrites the webhook with the label specified by `target_webhook_label` (default value: Gradio).
|
| 13 |
|
| 14 |
+
You need to pre-install gradio, fastapi, and pydantic.
|
| 15 |
+
You need to describe your Linear API key and Linear webhook secret in the `.env` file.
|
| 16 |
+
Also, since this example is only for Update, please create a Webhook with the label Gradio beforehand.
|
| 17 |
|
| 18 |
+
** Copyright Notice for Linear.app Adaptation **
|
| 19 |
# Copyright 2025-present, Akihito Miyazaki
|
| 20 |
#
|
| 21 |
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
| 30 |
# See the License for the specific language governing permissions and
|
| 31 |
# limitations under the License.
|
| 32 |
|
| 33 |
+
** License Notice for Hugging Face Hub Library **
|
| 34 |
# Copyright 2023-present, the HuggingFace Inc. team.
|
| 35 |
#
|
| 36 |
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
| 45 |
# See the License for the specific language governing permissions and
|
| 46 |
# limitations under the License.
|
| 47 |
|
| 48 |
+
This code includes parts of the Hugging Face Hub library, which is licensed under the Apache License, Version 2.0.
|
| 49 |
+
The full text of the license can be found at: http://www.apache.org/licenses/LICENSE-2.0
|
| 50 |
"""
|
| 51 |
|
| 52 |
"""Contains data structures to parse the webhooks payload."""
|
gradio_webhook_server.py
CHANGED
|
@@ -1,14 +1,21 @@
|
|
| 1 |
"""
|
| 2 |
-
|
| 3 |
-
Local Gradioの動作のみ確認・Spaceでのテストはまだしていません。
|
| 4 |
-
特に verify_signature 関数と、webhook 呼び出し時の request ヘッダーの取り扱いを変更しています。
|
| 5 |
https://github.com/huggingface/huggingface_hub/blob/main/src/huggingface_hub/_webhooks_server.py
|
|
|
|
| 6 |
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
webhook_request.json
|
| 10 |
|
| 11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
# Copyright 2025-present, Akihito Miyazaki
|
| 13 |
#
|
| 14 |
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -23,7 +30,7 @@ webhook_request.json
|
|
| 23 |
# See the License for the specific language governing permissions and
|
| 24 |
# limitations under the License.
|
| 25 |
|
| 26 |
-
** Hugging Face Hub
|
| 27 |
# Copyright 2023-present, the HuggingFace Inc. team.
|
| 28 |
#
|
| 29 |
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -38,8 +45,8 @@ webhook_request.json
|
|
| 38 |
# See the License for the specific language governing permissions and
|
| 39 |
# limitations under the License.
|
| 40 |
|
| 41 |
-
|
| 42 |
-
|
| 43 |
"""
|
| 44 |
|
| 45 |
"""Contains `WebhooksServer` and `webhook_endpoint` to create a webhook server easily."""
|
|
@@ -58,46 +65,46 @@ from fastapi.encoders import jsonable_encoder
|
|
| 58 |
|
| 59 |
def verify_signature(request_headers, payload, webhook_secret):
|
| 60 |
"""
|
| 61 |
-
|
| 62 |
|
| 63 |
Args:
|
| 64 |
-
request_headers:
|
| 65 |
-
payload:
|
| 66 |
-
webhook_secret:
|
| 67 |
|
| 68 |
Returns:
|
| 69 |
-
True:
|
| 70 |
-
False:
|
| 71 |
"""
|
| 72 |
|
| 73 |
-
#
|
| 74 |
-
#
|
| 75 |
-
#
|
|
|
|
| 76 |
|
| 77 |
-
#
|
| 78 |
if isinstance(payload, str):
|
| 79 |
payload = payload.encode("utf-8")
|
| 80 |
|
| 81 |
# HMAC署名を生成
|
| 82 |
signature = hmac.new(
|
| 83 |
-
webhook_secret.encode("utf-8"),
|
| 84 |
payload,
|
| 85 |
hashlib.sha256,
|
| 86 |
).hexdigest()
|
| 87 |
|
| 88 |
-
#
|
| 89 |
-
#
|
| 90 |
linear_signature = request_headers.get("linear-signature")
|
| 91 |
if not linear_signature:
|
| 92 |
linear_signature = request_headers.get(
|
| 93 |
"Linear-Signature"
|
| 94 |
-
) #
|
| 95 |
if not linear_signature:
|
| 96 |
print("Error: linear-signature header not found")
|
| 97 |
return False
|
| 98 |
|
| 99 |
-
|
| 100 |
-
return hmac.compare_digest(signature, linear_signature) # 脆弱性対策
|
| 101 |
|
| 102 |
|
| 103 |
# from .utils import experimental, is_fastapi_available, is_gradio_available
|
|
@@ -466,7 +473,7 @@ def _wrap_webhook_to_check_secret(func: Callable, webhook_secret: str) -> Callab
|
|
| 466 |
|
| 467 |
data = json.loads(request_text.decode())
|
| 468 |
|
| 469 |
-
#
|
| 470 |
with open("webhook_header.json", "w", encoding="utf-8") as f:
|
| 471 |
serialized = jsonable_encoder(request.headers)
|
| 472 |
json.dump(serialized, f, indent=2, ensure_ascii=False)
|
|
|
|
| 1 |
"""
|
| 2 |
+
This code adapts the following code for Linear.app. It is not official code.
|
|
|
|
|
|
|
| 3 |
https://github.com/huggingface/huggingface_hub/blob/main/src/huggingface_hub/_webhooks_server.py
|
| 4 |
+
https://github.com/huggingface/huggingface_hub/blob/main/src/huggingface_hub/_webhooks_payload.py
|
| 5 |
|
| 6 |
+
Currently, it only supports a small subset of the Issue Object.
|
| 7 |
+
https://studio.apollographql.com/public/Linear-API/variant/current/schema/reference/objects/Issue
|
|
|
|
| 8 |
|
| 9 |
+
You need to set `api_key = linear-api-key` and `webhook_secret = linear-webhook-secret` in your `.env` file.
|
| 10 |
+
|
| 11 |
+
Since the local URL changes with each startup, the URL is updated in the Linear API at startup.
|
| 12 |
+
At startup, it overwrites the webhook with the label specified by `target_webhook_label` (default value: Gradio).
|
| 13 |
+
|
| 14 |
+
You need to pre-install gradio, fastapi, and pydantic.
|
| 15 |
+
You need to describe your Linear API key and Linear webhook secret in the `.env` file.
|
| 16 |
+
Also, since this example is only for Update, please create a Webhook with the label Gradio beforehand.
|
| 17 |
+
|
| 18 |
+
** Copyright Notice for Linear.app Adaptation **
|
| 19 |
# Copyright 2025-present, Akihito Miyazaki
|
| 20 |
#
|
| 21 |
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
| 30 |
# See the License for the specific language governing permissions and
|
| 31 |
# limitations under the License.
|
| 32 |
|
| 33 |
+
** License Notice for Hugging Face Hub Library **
|
| 34 |
# Copyright 2023-present, the HuggingFace Inc. team.
|
| 35 |
#
|
| 36 |
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
| 45 |
# See the License for the specific language governing permissions and
|
| 46 |
# limitations under the License.
|
| 47 |
|
| 48 |
+
This code includes parts of the Hugging Face Hub library, which is licensed under the Apache License, Version 2.0.
|
| 49 |
+
The full text of the license can be found at: http://www.apache.org/licenses/LICENSE-2.0
|
| 50 |
"""
|
| 51 |
|
| 52 |
"""Contains `WebhooksServer` and `webhook_endpoint` to create a webhook server easily."""
|
|
|
|
| 65 |
|
| 66 |
def verify_signature(request_headers, payload, webhook_secret):
|
| 67 |
"""
|
| 68 |
+
Verifies if the request is valid by comparing the signature in the request header with the signature generated from the payload.
|
| 69 |
|
| 70 |
Args:
|
| 71 |
+
request_headers: Request headers (dict-like object, e.g., `request.headers`)
|
| 72 |
+
payload: Request payload (bytes or string)
|
| 73 |
+
webhook_secret: Webhook secret key (string)
|
| 74 |
|
| 75 |
Returns:
|
| 76 |
+
True: If the signatures match (request is valid)
|
| 77 |
+
False: If the signatures do not match (request is invalid)
|
| 78 |
"""
|
| 79 |
|
| 80 |
+
# Retrieve WEBHOOK_SECRET from environment variables
|
| 81 |
+
# (Alternative to Netlify.env.get('WEBHOOK_SECRET'))
|
| 82 |
+
# e.g., webhook_secret = os.environ.get('WEBHOOK_SECRET')
|
| 83 |
+
# Please adjust to match your actual environment variable name
|
| 84 |
|
| 85 |
+
# If the payload is a string, convert it to bytes
|
| 86 |
if isinstance(payload, str):
|
| 87 |
payload = payload.encode("utf-8")
|
| 88 |
|
| 89 |
# HMAC署名を生成
|
| 90 |
signature = hmac.new(
|
| 91 |
+
webhook_secret.encode("utf-8"),
|
| 92 |
payload,
|
| 93 |
hashlib.sha256,
|
| 94 |
).hexdigest()
|
| 95 |
|
| 96 |
+
# Retrieve the signature from the request headers
|
| 97 |
+
# Note that header names might be case-insensitive
|
| 98 |
linear_signature = request_headers.get("linear-signature")
|
| 99 |
if not linear_signature:
|
| 100 |
linear_signature = request_headers.get(
|
| 101 |
"Linear-Signature"
|
| 102 |
+
) # Check for case variations in header names.
|
| 103 |
if not linear_signature:
|
| 104 |
print("Error: linear-signature header not found")
|
| 105 |
return False
|
| 106 |
|
| 107 |
+
return hmac.compare_digest(signature, linear_signature)
|
|
|
|
| 108 |
|
| 109 |
|
| 110 |
# from .utils import experimental, is_fastapi_available, is_gradio_available
|
|
|
|
| 473 |
|
| 474 |
data = json.loads(request_text.decode())
|
| 475 |
|
| 476 |
+
# フ
|
| 477 |
with open("webhook_header.json", "w", encoding="utf-8") as f:
|
| 478 |
serialized = jsonable_encoder(request.headers)
|
| 479 |
json.dump(serialized, f, indent=2, ensure_ascii=False)
|
linear_api_utils.py
CHANGED
|
@@ -24,7 +24,7 @@ import json
|
|
| 24 |
import os
|
| 25 |
import time
|
| 26 |
|
| 27 |
-
#
|
| 28 |
from pprint import pprint
|
| 29 |
import requests
|
| 30 |
|
|
@@ -56,6 +56,8 @@ def request_linear(
|
|
| 56 |
|
| 57 |
def load_api_key(dir="./"):
|
| 58 |
print(f"{dir}.env")
|
|
|
|
|
|
|
| 59 |
load_dotenv(dotenv_path=f"{dir}.env")
|
| 60 |
if "api_key" in os.environ:
|
| 61 |
api_key = os.environ["api_key"]
|
|
|
|
| 24 |
import os
|
| 25 |
import time
|
| 26 |
|
| 27 |
+
#
|
| 28 |
from pprint import pprint
|
| 29 |
import requests
|
| 30 |
|
|
|
|
| 56 |
|
| 57 |
def load_api_key(dir="./"):
|
| 58 |
print(f"{dir}.env")
|
| 59 |
+
from dotenv import load_dotenv
|
| 60 |
+
|
| 61 |
load_dotenv(dotenv_path=f"{dir}.env")
|
| 62 |
if "api_key" in os.environ:
|
| 63 |
api_key = os.environ["api_key"]
|