From bb25c3dc2941e4e92449324cf73586ebba7b60af Mon Sep 17 00:00:00 2001 From: nogueiraguh Date: Sun, 8 Mar 2026 01:34:17 -0300 Subject: [PATCH] feat: robust TF file correction, move Start/Stop to Explorer, increase context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Inject existing workspace files + plan/apply output into Terraform chat context so model corrects in-place instead of regenerating from scratch - Add "REGRA CRÍTICA: Correção de Arquivos Existentes" to system prompt requiring the model to preserve file structure and fix surgically - Increase Terraform Agent max_tokens from 16K to 32K - Remove "Nova conversa" button from Terraform toolbar - Move Start/Stop Instance and ADB buttons from Terraform Resources panel to OCI Account Explorer (instances and databases tabs) - Remove unused _ociResOid helper and old action functions --- backend/app.py | 16 +++++++++++++++- frontend/index.html | 41 ++++++++++++++++++++++++++++++----------- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/backend/app.py b/backend/app.py index 66057d1..c9e5c2f 100644 --- a/backend/app.py +++ b/backend/app.py @@ -2038,6 +2038,20 @@ Abaixo estão os nomes corretos de resource types do provider oracle/oci. O mode - **DRG Route Table**: `oci_core_drg_route_table` - **DRG Route Distribution**: `oci_core_drg_route_distribution` - **DRG Route Table Route Rule**: `oci_core_drg_route_table_route_rule` + +### REGRA CRÍTICA: Correção de Arquivos Existentes +Quando a mensagem do usuário incluir a seção "ARQUIVOS TERRAFORM ATUAIS NO WORKSPACE", significa que JÁ EXISTEM arquivos gerados anteriormente. Neste caso: + +1. **NUNCA descarte os arquivos existentes**. Eles são o trabalho atual do usuário. +2. **Gere TODOS os arquivos completos** — mesmo os que não precisaram de alteração. O sistema substitui todos os arquivos do workspace pelo que você gerar. +3. **Corrija APENAS o que está errado**: se o erro é em `networking.tf`, mantenha `variables.tf`, `compute.tf`, `outputs.tf` etc. EXATAMENTE como estão e corrija apenas `networking.tf`. +4. **Analise o output do terraform plan/apply** se incluído na mensagem — ele mostra exatamente qual recurso, atributo ou argumento causou o erro. +5. **Mantenha a mesma estrutura de arquivos**: use os mesmos nomes de arquivo (`// filename: xxx.tf`) que já existem. NÃO renomeie, NÃO reorganize, NÃO mescle arquivos. +6. **Preserve nomes de recursos**: NÃO renomeie recursos existentes (ex: `oci_core_vcn.hub` não deve virar `oci_core_vcn.main`), pois isso causaria destroy+recreate desnecessário. +7. Se o erro for um resource type inválido, consulte a referência de recursos OCI neste prompt para encontrar o nome correto. +8. Se o erro for um argumento inválido (Unsupported argument), remova-o ou substitua pela sintaxe correta. + +**RESUMO**: Ao corrigir, regenere todos os arquivos mantendo conteúdo intacto nos que não têm erro, e corrija cirurgicamente os que têm problema. O resultado deve funcionar no `terraform plan` sem erros na primeira tentativa. """ # ── Terraform OCI Resource Reference (generated at build time, updatable at runtime) ── @@ -3168,7 +3182,7 @@ async def _chat_background(mid: str, sid: str, msg: ChatMsg, user: dict, genai_c # ── Terraform agent: boost max_tokens for complex infra code ── if agent_type == "terraform": - cfg_dict["max_tokens"] = max(int(cfg_dict.get("max_tokens", 6000)), 16000) + cfg_dict["max_tokens"] = max(int(cfg_dict.get("max_tokens", 6000)), 32000) # ── Inject existing OCI resources for terraform agent ── if agent_type == "terraform" and active_oci_id: diff --git a/frontend/index.html b/frontend/index.html index 7fd35e9..890e22d 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -800,7 +800,6 @@ function rTerraform(){ -
${ms}
@@ -997,12 +996,12 @@ function rTfResourcesPanel(){ const cats=[ {key:'vcns',label:'VCNs',icon:'',fmt:r=>`${r.display_name} (${(r.cidr_blocks||[]).join(', ')})`}, {key:'subnets',label:'Subnets',icon:'',fmt:r=>`${r.display_name} (${r.cidr_block}, ${r.public?'pública':'privada'})`}, - {key:'instances',label:'Compute',icon:'',fmt:r=>{const stopped=r.state==='STOPPED';const running=r.state==='RUNNING';const stC=stopped?'var(--rd)':running?'var(--gn)':'var(--t3)';return`${r.display_name} (${r.shape}) ${r.state}${stopped?` `:running?` `:''}`}}, + {key:'instances',label:'Compute',icon:'',fmt:r=>{const stopped=r.state==='STOPPED';const running=r.state==='RUNNING';const stC=stopped?'var(--rd)':running?'var(--gn)':'var(--t3)';return`${r.display_name} (${r.shape}) ${r.state}`}}, {key:'internet_gateways',label:'Internet GWs',icon:'',fmt:r=>r.display_name}, {key:'nat_gateways',label:'NAT GWs',icon:'',fmt:r=>r.display_name}, {key:'route_tables',label:'Route Tables',icon:'',fmt:r=>r.display_name}, {key:'security_lists',label:'Security Lists',icon:'',fmt:r=>r.display_name}, - {key:'autonomous_databases',label:'Databases',icon:'',fmt:r=>{const stopped=r.state==='STOPPED';const avail=r.state==='AVAILABLE';const stC=stopped?'var(--rd)':avail?'var(--gn)':'var(--t3)';return`${r.display_name} (${r.db_name}${r.is_free_tier?' Free':''}) ${r.state}${stopped?` `:avail?` `:''}`}}, + {key:'autonomous_databases',label:'Databases',icon:'',fmt:r=>{const stopped=r.state==='STOPPED';const avail=r.state==='AVAILABLE';const stC=stopped?'var(--rd)':avail?'var(--gn)':'var(--t3)';return`${r.display_name} (${r.db_name}${r.is_free_tier?' Free':''}) ${r.state}`}}, {key:'block_volumes',label:'Block Volumes',icon:'',fmt:r=>`${r.display_name} (${r.size_in_gbs||'?'} GB)`}, {key:'load_balancers',label:'Load Balancers',icon:'',fmt:r=>`${r.display_name} (${r.shape_name||'flexible'})`} ]; @@ -1025,11 +1024,16 @@ function rTfResourcesPanel(){ html+='
'; return html; } -function _ociResOid(){return S.tfOci||(S.tfModel.startsWith('cfg:')?((S.genaiCfg.find(x=>x.id===S.tfModel.slice(4))||{}).oci_config_id||''):'')} -async function ociStartInstance(id){if(!confirm('Iniciar esta instância?'))return;try{await $api('/oci/instances/'+id+'/action',{method:'POST',body:{action:'START',oci_config_id:_ociResOid(),region:S.tfRegion||null}});alert('Instância iniciando...');tfLoadResources()}catch(e){alert('Erro: '+e.message)}} -async function ociStopInstance(id){if(!confirm('Parar esta instância?'))return;try{await $api('/oci/instances/'+id+'/action',{method:'POST',body:{action:'STOP',oci_config_id:_ociResOid(),region:S.tfRegion||null}});alert('Instância parando...');tfLoadResources()}catch(e){alert('Erro: '+e.message)}} -async function ociStartAdb(id){if(!confirm('Iniciar este Autonomous Database?'))return;try{await $api('/oci/autonomous-databases/'+id+'/action',{method:'POST',body:{action:'start',oci_config_id:_ociResOid(),region:S.tfRegion||null}});alert('Autonomous DB iniciando...');tfLoadResources()}catch(e){alert('Erro: '+e.message)}} -async function ociStopAdb(id){if(!confirm('Parar este Autonomous Database?'))return;try{await $api('/oci/autonomous-databases/'+id+'/action',{method:'POST',body:{action:'stop',oci_config_id:_ociResOid(),region:S.tfRegion||null}});alert('Autonomous DB parando...');tfLoadResources()}catch(e){alert('Erro: '+e.message)}} +async function ociInstanceAction(id,action){ + const label=action==='START'?'Iniciar':'Parar'; + if(!confirm(label+' esta instância?'))return; + try{await $api('/oci/instances/'+id+'/action',{method:'POST',body:{action,oci_config_id:S.expCfg,region:null}});alert('Instância '+(action==='START'?'iniciando':'parando')+'...');expLoadRes()}catch(e){alert('Erro: '+e.message)} +} +async function ociAdbAction(id,action){ + const label=action==='start'?'Iniciar':'Parar'; + if(!confirm(label+' este Autonomous Database?'))return; + try{await $api('/oci/autonomous-databases/'+id+'/action',{method:'POST',body:{action,oci_config_id:S.expCfg,region:null}});alert('Autonomous DB '+(action==='start'?'iniciando':'parando')+'...');expLoadRes()}catch(e){alert('Erro: '+e.message)} +} function tfSwitchBtab(tab){ S.tfBtab=tab;S.tfEditIdx=-1; if(tab==='resources'&&!S.tfResources&&S.tfCompartment)tfLoadResources(); @@ -1051,10 +1055,18 @@ async function tfSend(){ const el=document.getElementById('tfi');const m=el.value.trim();if(!m)return;el.style.height='auto';S._tfiVal=''; if(!S.tfModel){S.tfMsgs.push({r:'assistant',c:'⚠️ Selecione um modelo antes de enviar.',t:tstamp()});R();return} if(!S.tfCompartment){S.tfMsgs.push({r:'assistant',c:'⚠️ Selecione um compartment antes de enviar.',t:tstamp()});R();return} - el.value='';S.tfMsgs.push({r:'user',c:m,t:tstamp()});R();tfScroll(); + el.value='';S.tfMsgs.push({r:'user',c:m,t:tstamp()});R();tfScroll(); // show original msg without injected files S.tfMsgs.push({r:'assistant',c:'⏳ _Gerando Terraform..._',thinking:true});R();tfScroll(); try{ - const body={message:m,session_id:S.tfSid,use_tools:false}; + // Inject existing files context when user is correcting/fixing + let finalMsg=m; + if(S.tfFiles.length>0){ + const filesCtx=S.tfFiles.map(f=>'// filename: '+f.name+'\n'+f.content).join('\n\n'); + finalMsg+='\n\n---\nARQUIVOS TERRAFORM ATUAIS NO WORKSPACE (corrija estes arquivos):\n```hcl\n'+filesCtx+'\n```'; + } + if(S.tfPlanOut){finalMsg+='\n\n---\nÚLTIMO OUTPUT DO TERRAFORM PLAN:\n```\n'+S.tfPlanOut.slice(-3000)+'\n```'} + if(S.tfApplyOut){finalMsg+='\n\n---\nÚLTIMO OUTPUT DO TERRAFORM APPLY:\n```\n'+S.tfApplyOut.slice(-3000)+'\n```'} + const body={message:finalMsg,session_id:S.tfSid,use_tools:false}; if(S.tfModel.startsWith('cfg:'))body.genai_config_id=S.tfModel.slice(4); else{ if(!S.tfOci){S.tfMsgs.pop();S.tfMsgs.push({r:'assistant',c:'⚠️ Selecione uma credencial OCI.',t:tstamp()});R();return} @@ -1264,11 +1276,18 @@ function renderExpData(d){if(!d)return'

Selecione um compartm if(d.error)return`

${d.error}
`;if(!Array.isArray(d)||!d.length)return'

Nenhum recurso encontrado neste compartment.

'; if(S.expResType==='security_lists')return rExpSecLists(d); if(S.expResType==='route_tables')return rExpRouteTables(d); + const isInst=S.expResType==='instances'; + const isAdb=S.expResType==='databases'; return`
${d.length} item(ns) encontrado(s)
${d.map(i=>{ const keys=Object.keys(i); const name=i.display_name||i.name||i.db_name||i.application||'—'; const skip=['display_name','name','db_name','lifecycle_state','application','rules','routes']; - return`
${name}${i.lifecycle_state?` ${i.lifecycle_state}`:''}
+ let actionBtn=''; + if(isInst&&i.lifecycle_state==='RUNNING')actionBtn=' '; + else if(isInst&&i.lifecycle_state==='STOPPED')actionBtn=' '; + else if(isAdb&&i.lifecycle_state==='AVAILABLE')actionBtn=' '; + else if(isAdb&&i.lifecycle_state==='STOPPED')actionBtn=' '; + return`
${name}${i.lifecycle_state?` ${i.lifecycle_state}`:''}${actionBtn}
${keys.filter(k=>!skip.includes(k)).map(k=>`${k}: ${typeof i[k]==='object'?JSON.stringify(i[k]):i[k]}`).join('
')}
`}).join('')}
`} function rExpSecLists(d){ return`
${d.length} security list(s)
${d.map((sl,idx)=>{