Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f85944646 | ||
|
|
2154bba21d | ||
|
|
69859f0084 | ||
|
|
fc5e2a4dc1 | ||
|
|
d287de8ed2 | ||
|
|
d4dfd10f9a | ||
|
|
723829d38c | ||
|
|
6c7c1d38a1 | ||
|
|
3aa8e9d205 | ||
|
|
6dbc1405c6 | ||
|
|
16458aea92 | ||
|
|
463cf697fd | ||
|
|
c369804d3c | ||
|
|
0ccce55ab0 | ||
|
|
c53f4c9049 | ||
|
|
2bb3b60f18 | ||
|
|
70abc8e7ff | ||
|
|
693e7a51e7 | ||
|
|
f8fac5946b | ||
|
|
2ba1ee3416 | ||
|
|
623a29bafd | ||
|
|
1bc19e4c90 | ||
|
|
b2c0af564f | ||
|
|
bc6fb0ffe5 | ||
|
|
2acde6ae75 | ||
|
|
a88c47e273 | ||
|
|
a0047a012d | ||
|
|
7b47faac50 | ||
|
|
c6ff7be55a | ||
|
|
f5dd3e5874 | ||
|
|
ac07c7f06a | ||
|
|
230d47cade | ||
|
|
5732229ed5 | ||
|
|
ffb5825407 | ||
|
|
a02401aa75 | ||
|
|
edf3a83e3c | ||
|
|
9799aa00c5 | ||
|
|
456c443e9c | ||
|
|
eb7717e9ce | ||
|
|
231af72de9 | ||
|
|
6e0ced4772 | ||
|
|
aead65192e | ||
|
|
075492e391 | ||
|
|
8efb4faf73 | ||
|
|
2976bf2030 | ||
|
|
9cef1d9ba1 | ||
|
|
91db488ad4 | ||
|
|
eb1d62f85c | ||
|
|
7f27812efd | ||
|
|
d962c768a5 | ||
|
|
7f8f607f26 | ||
|
|
4df3254569 |
No files matched your search
@@ -1,2 +0,0 @@
|
||||
*.json filter=lfs diff=lfs merge=lfs -text
|
||||
llm_model_* filter=lfs diff=lfs merge=lfs -text
|
||||
@@ -1,36 +0,0 @@
|
||||
# xiaoli-llm-chat
|
||||
|
||||
#### Description
|
||||
小狸AI - 自研中文对话大模型
|
||||
|
||||
#### Software Architecture
|
||||
Software architecture description
|
||||
|
||||
#### Installation
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### Instructions
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### Contribution
|
||||
|
||||
1. Fork the repository
|
||||
2. Create Feat_xxx branch
|
||||
3. Commit your code
|
||||
4. Create Pull Request
|
||||
|
||||
|
||||
#### Gitee Feature
|
||||
|
||||
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
|
||||
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
|
||||
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
|
||||
4. The most valuable open source project [GVP](https://gitee.com/gvp)
|
||||
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
|
||||
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
|
||||
@@ -1,37 +1,17 @@
|
||||
# xiaoli-llm-chat
|
||||
# 项目已迁移 ✨
|
||||
|
||||
#### 介绍
|
||||
小狸AI - 自研中文对话大模型
|
||||
你好呀~这个仓库已经搬到更好用的地方啦。
|
||||
|
||||
#### 软件架构
|
||||
软件架构说明
|
||||
为了让代码管理更清爽、多平台同步更省心,我们把原先分散的镜像统一收拢到主仓维护,这里就只保留这张"迁移小纸条"咯。
|
||||
|
||||
📦 新家在这里:
|
||||
- Gitee:https://gitee.com/shuiyu1123/xiaoli-llm-chat
|
||||
- GitHub:https://github.com/shuiyu67/xiaoli-llm-chat
|
||||
|
||||
#### 安装教程
|
||||
📋 内部评估:
|
||||
- 镜像分散在多个平台,重复维护成本高、容易漏更;
|
||||
- 自托管 Gitea 实例为个人非管理员环境,长期可用性与备份缺乏保障;
|
||||
- 代码已稳定收拢至 Gitee / GitHub 主仓,原镜像不再承载更新职责;
|
||||
- 统一主仓后便于协作、CI 与备份,减少多端同步负担。
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### 使用说明
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### 参与贡献
|
||||
|
||||
1. Fork 本仓库
|
||||
2. 新建 Feat_xxx 分支
|
||||
3. 提交代码
|
||||
4. 新建 Pull Request
|
||||
|
||||
|
||||
#### 特技
|
||||
|
||||
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
|
||||
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
|
||||
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
|
||||
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
|
||||
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
|
||||
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
|
||||
旧镜像不再更新,但代码都好好的在新家继续生长。感谢你一路的关注与陪伴 💛
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:136863dba34ed2300a56611600b9cd758aa6e01cdd7e3dcdd35d0371be32d41c
|
||||
size 43711
|
||||
-143
@@ -1,143 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
小狸聊天 Web 服务 v2 - 支持多轮对话
|
||||
"""
|
||||
import sys, os, json, re
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from flask import Flask, render_template, request, jsonify
|
||||
import torch
|
||||
from llm_chat_improved import (
|
||||
CharTokenizer, build_vocabulary, LLMModel, LLMChat, load_training_data
|
||||
)
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# 全局模型
|
||||
llm_chat = None
|
||||
vocab = None
|
||||
|
||||
def clean_text(text):
|
||||
"""清洗文本"""
|
||||
text = text.replace('\n', '')
|
||||
text = re.sub(r'\*\*([^*]+)\*\*', r'\1', text)
|
||||
text = re.sub(r'\([^)]*[ω。ノ♡^][^)]*\)', '', text)
|
||||
emoji_pattern = re.compile(
|
||||
'[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\U0001F680-\U0001F6FF\U0001F1E0-\U0001F1FF\U00002702-\U000027B0\U0001f926-\U0001f937\u2640-\u2642\u2600-\u2B55\u200d\u23cf\u23e9\u231a\ufe0f\u3030\u2728\u2714\u2716\u27a1\u2b05\u2b06\u2b07\u2b1b\u2b1c\u2b50\u2b55\u2934\u2935\u25aa\u25ab\u25b6\u25c0\u25fb\u25fc\u25fd\u25fe]+',
|
||||
flags=re.UNICODE
|
||||
)
|
||||
text = emoji_pattern.sub('', text)
|
||||
text = re.sub(r'[ \t]+', ' ', text)
|
||||
return text.strip()
|
||||
|
||||
def load_model():
|
||||
"""加载训练好的模型"""
|
||||
global llm_chat, vocab
|
||||
|
||||
print("🐱 正在加载小狸模型...")
|
||||
|
||||
device = 'cpu'
|
||||
|
||||
# 直接从模型文件加载 vocab 和配置 (v4大模型)
|
||||
checkpoint = torch.load('xiaoli_final_v4.pth', map_location=device)
|
||||
vocab = checkpoint['vocab']
|
||||
config = checkpoint.get('model_config', {})
|
||||
|
||||
print(f" 词汇表大小: {len(vocab)}")
|
||||
|
||||
# 创建模型 - 使用保存的配置(v4大模型带MoE+Thinking+Memory)
|
||||
model = LLMModel(
|
||||
vocab_size=config.get('vocab_size', len(vocab)),
|
||||
d_model=config.get('d_model', 384),
|
||||
n_heads=config.get('n_heads', 8),
|
||||
n_layers=config.get('n_layers', 6),
|
||||
d_ff=config.get('d_ff', 1536),
|
||||
max_len=config.get('max_len', 128),
|
||||
dropout=0.0,
|
||||
use_memory=config.get('use_memory', True),
|
||||
use_thinking=config.get('use_thinking', True),
|
||||
use_moe=config.get('use_moe', True),
|
||||
num_experts=config.get('num_experts', 4),
|
||||
top_k=config.get('top_k', 2),
|
||||
)
|
||||
|
||||
# 加载权重
|
||||
model.load_state_dict(checkpoint['model_state_dict'])
|
||||
|
||||
llm_chat = LLMChat(model, vocab, device, 128)
|
||||
print("✅ 小狸已就绪!")
|
||||
|
||||
# 存储会话历史
|
||||
session_history = {}
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('chat.html')
|
||||
|
||||
@app.route('/chat', methods=['POST'])
|
||||
def chat():
|
||||
data = request.json
|
||||
message = data.get('message', '').strip()
|
||||
session_id = data.get('session_id', 'default')
|
||||
|
||||
if not message:
|
||||
return jsonify({'response': '请输入内容哦~'})
|
||||
|
||||
if llm_chat is None:
|
||||
return jsonify({'response': '模型加载中,请稍后再试'})
|
||||
|
||||
try:
|
||||
# 获取会话历史
|
||||
history = session_history.get(session_id, [])
|
||||
|
||||
# 构建带上下文的输入(使用与训练一致的格式)
|
||||
# 训练格式: <SOS>问题<SEP><START>
|
||||
# 多轮格式: <SOS>历史问题1<SEP>历史回答1<SEP>历史问题2<SEP>历史回答2<SEP>当前问题<SEP><START>
|
||||
if history and len(history) >= 2:
|
||||
# 只保留最近2轮对话(4条记录)
|
||||
recent_history = history[-4:]
|
||||
# 提取问题和答案
|
||||
context_parts = []
|
||||
for i in range(0, len(recent_history), 2):
|
||||
if i + 1 < len(recent_history):
|
||||
q = recent_history[i].replace("用户:", "")
|
||||
a = recent_history[i+1].replace("小狸:", "")
|
||||
context_parts.append(f"{q}<SEP>{a}")
|
||||
|
||||
if context_parts:
|
||||
context = "<SEP>".join(context_parts)
|
||||
full_input = f"{context}<SEP>{message}"
|
||||
else:
|
||||
full_input = message
|
||||
else:
|
||||
full_input = message
|
||||
|
||||
# 限制输入长度(字符数)
|
||||
if len(full_input) > 80:
|
||||
full_input = full_input[-80:]
|
||||
|
||||
response = llm_chat.generate_response(full_input, max_length=50, temperature=0.3)
|
||||
|
||||
# 更新历史
|
||||
history.append(f"用户:{message}")
|
||||
history.append(f"小狸:{response}")
|
||||
# 只保留最近6轮(12条记录)
|
||||
session_history[session_id] = history[-12:]
|
||||
|
||||
return jsonify({'response': response})
|
||||
except Exception as e:
|
||||
import traceback
|
||||
return jsonify({'response': f'出错了: {str(e)}\n{traceback.format_exc()}'})
|
||||
|
||||
@app.route('/clear', methods=['POST'])
|
||||
def clear_history():
|
||||
data = request.json
|
||||
session_id = data.get('session_id', 'default')
|
||||
session_history[session_id] = []
|
||||
return jsonify({'status': 'ok'})
|
||||
|
||||
if __name__ == '__main__':
|
||||
load_model()
|
||||
print("\n🌐 启动 Web 服务: http://0.0.0.0:5000")
|
||||
app.run(host='0.0.0.0', port=5000, debug=False)
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7c4d05531958838c1c6750216a98fc781ea13085b278a0154eb1378680959fe9
|
||||
size 10038
|
||||
@@ -1,309 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>小狸 - AI 聊天</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.chat-container {
|
||||
width: 90%;
|
||||
max-width: 500px;
|
||||
height: 80vh;
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
.chat-header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.chat-header h1 {
|
||||
font-size: 24px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.chat-header p {
|
||||
font-size: 12px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
.chat-messages {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 20px;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.message {
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.message.user {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.message.bot {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.message-content {
|
||||
max-width: 70%;
|
||||
padding: 12px 16px;
|
||||
border-radius: 18px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.message.user .message-content {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
.message.bot .message-content {
|
||||
background: white;
|
||||
color: #333;
|
||||
border-bottom-left-radius: 4px;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
||||
}
|
||||
.avatar {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
margin: 0 8px;
|
||||
}
|
||||
.chat-input {
|
||||
padding: 15px 20px;
|
||||
background: white;
|
||||
border-top: 1px solid #eee;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
.chat-input input {
|
||||
flex: 1;
|
||||
padding: 12px 16px;
|
||||
border: 2px solid #e0e0e0;
|
||||
border-radius: 25px;
|
||||
font-size: 14px;
|
||||
outline: none;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
.chat-input input:focus {
|
||||
border-color: #667eea;
|
||||
}
|
||||
.chat-input button {
|
||||
padding: 12px 24px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 25px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
.chat-input button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
.chat-input button:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
.typing {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
.typing span {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: #999;
|
||||
border-radius: 50%;
|
||||
animation: typing 1.4s infinite;
|
||||
}
|
||||
.typing span:nth-child(2) { animation-delay: 0.2s; }
|
||||
.typing span:nth-child(3) { animation-delay: 0.4s; }
|
||||
@keyframes typing {
|
||||
0%, 60%, 100% { transform: translateY(0); }
|
||||
30% { transform: translateY(-10px); }
|
||||
}
|
||||
.start-overlay {
|
||||
position: fixed;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,0.6);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
.start-overlay.hidden {
|
||||
display: none;
|
||||
}
|
||||
.start-card {
|
||||
background: white;
|
||||
border-radius: 24px;
|
||||
padding: 50px 40px;
|
||||
text-align: center;
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
||||
animation: popIn 0.5s ease;
|
||||
}
|
||||
@keyframes popIn {
|
||||
0% { transform: scale(0.8); opacity: 0; }
|
||||
100% { transform: scale(1); opacity: 1; }
|
||||
}
|
||||
.start-avatar {
|
||||
font-size: 64px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.start-card h2 {
|
||||
color: #333;
|
||||
font-size: 28px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.start-card p {
|
||||
color: #666;
|
||||
font-size: 16px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.start-btn {
|
||||
padding: 16px 40px;
|
||||
font-size: 18px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 30px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
.start-btn:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 10px 25px rgba(102, 126, 234, 0.5);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="chat-container">
|
||||
<div class="chat-header">
|
||||
<h1>🐱 小狸</h1>
|
||||
<p>你的 AI 小伙伴</p>
|
||||
</div>
|
||||
<div class="chat-messages" id="messages">
|
||||
<div class="message bot">
|
||||
<div class="avatar">🐱</div>
|
||||
<div class="message-content">你好!我是小狸,很高兴见到你!有什么想聊的吗?</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-input">
|
||||
<input type="text" id="userInput" placeholder="输入消息..." maxlength="100">
|
||||
<button id="sendBtn" onclick="sendMessage()">发送</button>
|
||||
</div>
|
||||
<div class="start-overlay" id="startOverlay">
|
||||
<div class="start-card">
|
||||
<div class="start-avatar">🐱</div>
|
||||
<h2>小狸已就绪!</h2>
|
||||
<p>你的 AI 小伙伴正在等你聊天~</p>
|
||||
<button class="start-btn" onclick="startChat()">👉 点击开始聊天</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const messagesDiv = document.getElementById('messages');
|
||||
const userInput = document.getElementById('userInput');
|
||||
const sendBtn = document.getElementById('sendBtn');
|
||||
const sessionId = 'session_' + Math.random().toString(36).substr(2, 9);
|
||||
|
||||
function addMessage(text, isUser) {
|
||||
const div = document.createElement('div');
|
||||
div.className = `message ${isUser ? 'user' : 'bot'}`;
|
||||
div.innerHTML = `
|
||||
${isUser ? '' : '<div class="avatar">🐱</div>'}
|
||||
<div class="message-content">${escapeHtml(text)}</div>
|
||||
${isUser ? '<div class="avatar">👤</div>' : ''}
|
||||
`;
|
||||
messagesDiv.appendChild(div);
|
||||
messagesDiv.scrollTop = messagesDiv.scrollHeight;
|
||||
}
|
||||
|
||||
function showTyping() {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'message bot';
|
||||
div.id = 'typing-indicator';
|
||||
div.innerHTML = `
|
||||
<div class="avatar">🐱</div>
|
||||
<div class="message-content">
|
||||
<div class="typing">
|
||||
<span></span><span></span><span></span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
messagesDiv.appendChild(div);
|
||||
messagesDiv.scrollTop = messagesDiv.scrollHeight;
|
||||
}
|
||||
|
||||
function hideTyping() {
|
||||
const typing = document.getElementById('typing-indicator');
|
||||
if (typing) typing.remove();
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
async function sendMessage() {
|
||||
const text = userInput.value.trim();
|
||||
if (!text) return;
|
||||
|
||||
addMessage(text, true);
|
||||
userInput.value = '';
|
||||
sendBtn.disabled = true;
|
||||
showTyping();
|
||||
|
||||
try {
|
||||
const response = await fetch('/chat', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ message: text, session_id: sessionId })
|
||||
});
|
||||
const data = await response.json();
|
||||
hideTyping();
|
||||
addMessage(data.response, false);
|
||||
} catch (error) {
|
||||
hideTyping();
|
||||
addMessage('抱歉,我出错了,请稍后再试~', false);
|
||||
}
|
||||
|
||||
sendBtn.disabled = false;
|
||||
}
|
||||
|
||||
userInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') sendMessage();
|
||||
});
|
||||
|
||||
function startChat() {
|
||||
document.getElementById('startOverlay').classList.add('hidden');
|
||||
userInput.focus();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c0c0848f74cc7604d41699134a5f34f54d5958779a6653e27973ef495790c29c
|
||||
size 5267
|
||||
Reference in new issue
Block a user